Skip to content

New tutorial: How to setup a Nginx webserver using ansible on Ubuntu #1032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
350 changes: 350 additions & 0 deletions tutorials/devops-recipe-using-ansible-to-build-a-web-server/01.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
---
SPDX-License-Identifier: MIT
path: "/tutorials/devops-recipe-using-ansible-to-build-a-web-server"
slug: "devops-recipe-using-ansible-to-build-a-web-server"
date: "2025-01-16"
title: "How to setup an Nginx webserver using Ansible on Ubuntu"
short_description: "This tutorial is for beginner DevOps enthusiasts and it shows how to simply setup an Nginx web server, install Docker and configure a Let’s Encrypt certificate"
tags: ["Ansible", "Docker", "Nginx", "Certbot"]
author: "Sergiu Chilat"
author_link: "https://github.com/sergiuchilat"
author_img: "https://avatars.githubusercontent.com/u/14886372"
author_description: "IT enthusiast for almost 20 years:)"
language: "en"
available_languages: ["en"]
header_img: "header-4"
cta: "dedicated"
---

## 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](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html))
- Has an SSH key pair
- A remote VPS instance with Ubuntu (see this [getting started](https://docs.hetzner.com/cloud/servers/getting-started/creating-a-server/))
- Add the public key of your local device to your remote VPS. You can find a simple tutorial [here](https://community.hetzner.com/tutorials/add-ssh-key-to-your-hetzner-cloud).

## 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.

```ini
[defaults]
inventory = inventory
```

* `~/ansible-project/inventory`

Here you can add the IP addresses of the servers that you want to configure.

```ini
[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.

```yaml
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

* `~/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`

```yaml
- 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`

```yaml
- 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`

```yaml
- 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.

```yaml

- 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 generation
- `APP_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:

```bash
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.

##### License: MIT

<!--

Contributor's Certificate of Origin

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I have
the right to submit it under the license indicated in the file; or

(b) The contribution is based upon previous work that, to the best of my
knowledge, is covered under an appropriate license and I have the
right under that license to submit that work with modifications,
whether created in whole or in part by me, under the same license
(unless I am permitted to submit under a different license), as
indicated in the file; or

(c) The contribution was provided directly to me by some other person
who certified (a), (b) or (c) and I have not modified it.

(d) I understand and agree that this project and the contribution are
public and that a record of the contribution (including all personal
information I submit with it, including my sign-off) is maintained
indefinitely and may be redistributed consistent with this project
or the license(s) involved.

Signed-off-by: Sergiu Chilat <chilatsergiu@gmail.com>

-->
Loading