Introduction
This tutorial is for beginner DevOps enthusiasts and it illustrate how to simply setup an Nginx web server, install Docker and DockerCompose, and configure a Let’s Encrypt certificate managed by Certbot. The proposed solution is based on Ansible playbooks.
Prerequisites
- A local device
- Has Ansible installed (see Ansible documentation)
- Has an SSH key pair
- A remote VPS instance with Ubuntu (see this getting started)
- Add the public key of your local device to your remote VPS. You can find a simple tutorial here.
Before you start
Before you start, make sure you have the following files and directories:
ansible-project/
├── ansible.cfg # Ansible configuration file
├── inventory # Ansible inventory
├── env.yml # Environment variables
└── playbooks/ # Directory for the playbooks that will be added in the steps below
-
~/ansible-project/ansible.cfg
Here you can set the inventory file that Ansible will use by default when running the playbooks.
[defaults] inventory = inventory
-
~/ansible-project/inventory
Here you can add the IP addresses of the servers that you want to configure.
[all] 127.0.0.1 127.0.0.2 127.0.0.3
-
~/ansible-project/env.yml
This project uses some variables that are stored in the
env.yml
file that is imported in all playbooks.APP_MAIN_DOMAIN: "example.com" LETS_ENCRYPT_EMAIL: "your_email@mail.com" NGINX_MAX_BODY_SIZE: "50M" TIMEZONE: "Europe/Chisinau"
- APP_MAIN_DOMAIN:
example.com
- the main domain that will be used for the server - LETS_ENCRYPT_EMAIL:
your_email@example.com
- the email that will be used for the Let's Encrypt certificate generation - NGINX_MAX_BODY_SIZE:
50M
- the maximum body size that Nginx will accept - TIMEZONE:
Europe/Chisinau
- the timezone that will be set on the server
- APP_MAIN_DOMAIN:
-
~/ansible-project/playbooks
Create a directory for the playbooks.
Step 1 - Configure the web sever
In this sample the timezone of the server is set to the TIMEZONE
variable. This playbook can be extended to set other configurations as well, depending on your needs.
~/ansible-project/playbooks/configure-server.yml
- name: Set the system timezone
hosts: all
become: yes
user: root
vars_files:
- ../env.yml
tasks:
- name: Set timezone
ansible.builtin.command:
cmd: timedatectl set-timezone {{ TIMEZONE }}
Step 2 - Install and configure Nginx
In this step we will install Nginx and configure it to serve the main domain. Below is the code that will install Nginx and configure it to accept a maximum body size of 50M.
~/ansible-project/playbooks/install-nginx.yml
- name: Install Nginx
hosts: all
become: yes
user: root
vars_files:
- ../env.yml
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: nginx
state: latest
- name: Ensure client_max_body_size is set inside the HTTP block
ansible.builtin.blockinfile:
path: /etc/nginx/nginx.conf
marker: "# {mark} ANSIBLE MANAGED BLOCK"
block: |
client_max_body_size {{ NGINX_MAX_BODY_SIZE }};
insertafter: '^http\s*{'
backup: yes
- name: Reload Nginx to apply changes
ansible.builtin.systemd:
name: nginx
state: reloaded
Step 3 - Install Docker and Docker Compose
The following code will install Docker and Docker compose to your server.
~/ansible-project/playbooks/install-docker.yml
- name: Install Docker on Ubuntu
hosts: all
remote_user: root
become: true
vars:
arch_mapping: # Map ansible architecture {{ ansible_architecture }} names to Docker's architecture names
x86_64: amd64
aarch64: arm64
tasks:
- name: Update and upgrade all packages to the latest version
ansible.builtin.apt:
update_cache: true
upgrade: dist
cache_valid_time: 3600
- name: Install required packages
ansible.builtin.apt:
pkg:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- software-properties-common
- name: Create directory for Docker's GPG key
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Add Docker's official GPG key
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
- name: Print architecture variables
ansible.builtin.debug:
msg: "Architecture: {{ ansible_architecture }}, Codename: {{ ansible_lsb.codename }}"
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: >-
deb [arch={{ arch_mapping[ansible_architecture] | default(ansible_architecture) }}
signed-by=/etc/apt/keyrings/docker.gpg]
https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable
filename: docker
state: present
- name: Install Docker and related packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: true
loop:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
- name: Add Docker group
ansible.builtin.group:
name: docker
state: present
- name: Add user to Docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: true
- name: Enable and start Docker services
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
loop:
- docker.service
- containerd.service
Step 4 - Install Certbot and configure Let’s Encrypt certificate
~/ansible-project/playbooks/install-certbot.yml
The following code will install Certbot and configure a Let's Encrypt certificate for the main domain.
- name: Secure the site with Let's Encrypt
hosts: all
become: true
user: root
vars_files:
- ../env.yml
vars:
webroot_path: '/var/www/html' # the root path of your site
certbot_source_directory: /usr/local/certbot-src
certbot_executable_path: "{{ certbot_source_directory }}/venv/bin/certbot"
email: "{{ LETS_ENCRYPT_EMAIL }}"
domain: "{{ APP_MAIN_DOMAIN }}"
tasks:
- name: Install required packages for certbot
ansible.builtin.apt:
name:
- python3
- python3-venv
- gcc
- libaugeas0
- libssl-dev
- libffi-dev
- ca-certificates
- openssl
- git
- name: Clone the certbot source directory
ansible.builtin.git:
depth: 1
repo: https://github.com/certbot/certbot
dest: "{{ certbot_source_directory }}"
update: true
- name: Create certbot in virtual environment
ansible.builtin.command: python3 tools/venv.py
args:
chdir: "{{ certbot_source_directory }}"
- name: Generate the SSL certificate
ansible.builtin.command: "{{ certbot_executable_path }} --nginx -d {{ domain }} --non-interactive --agree-tos --email {{ email }}"
- name: Set up automatic renewal
ansible.builtin.cron:
name: "Certbot automatic renewal"
job: "{{ certbot_executable_path }} renew --quiet"
minute: "11"
hour: "11"
You can find the variables used in this playbook in the env.yml
file:
LETS_ENCRYPT_EMAIL
- the email that will be used for the Let's Encrypt certificate generationAPP_MAIN_DOMAIN
- the main domain that will be used for the server and the Let's Encrypt certificate
Step 5 - Run the playbooks
To run the playbooks, execute the following commands:
cd ~/ansible-project
ansible-playbook ~/ansible-project/playbooks/configure-server.yml
ansible-playbook ~/ansible-project/playbooks/install-nginx.yml
ansible-playbook ~/ansible-project/playbooks/install-docker.yml
ansible-playbook ~/ansible-project/playbooks/install-certbot.yml
After running the playbooks, you should have a web server running Nginx, Docker, Docker Compose and a Let's Encrypt certificate.
You can find the Let's Encrypt certificate on the remote VPS instance in /etc/letsencrypt/live/example.com
.
Conclusion
This tutorial is a simple way to setup a web server using Ansible playbooks. The playbooks can be extended to include more configurations and services.