Deploy a web server apache httpd virtual host on Debian-like systems - Ansible modules apt, file, copy, template, command, ufw and service
How to automate the deployment of a web server apache httpd virtual host "example.com" on Debian-like systems with custom web page taking care of downloading, installing, and enabling the service instantly and on boot and open the relevant firewall ports with Ansible modules apt, file, copy, template, command, ufw, and service. Debian, Ubuntu all the similar distributions.


How to deploy a webserver apache httpd virtual host on Debian-like systems with Ansible?
I’m going to show you a live demo with some simple Ansible code. I’m Luca Berton and welcome to today’s episode of Ansible Pilot.
Deploy a web server apache httpd virtual host on Debian-like systems
- install packages => ansible.builtin.apt
- document root => ansible.builtin.file
- custom index.html => ansible.builtin.copy
- Apache virtualhost => ansible.builtin.template
- enable new site => ansible.builtin.command
- open firewall => community.general.ufw
- reload service => ansible.builtin.service
Today we’re talking about how to Deploy a web server apache httpd on Debian-like Linux systems.
The full process requires seven steps that you could automate with different Ansible modules.
Firstly you need to install the apache2
package and dependency using the ansible.builtin.apt
Ansible module.
Secondly, you need to create the document root with the right permission with the ansible.builtin.file
module.
Thirsty, you need to create the custom index.html with the ansible.builtin.copy
Ansible module. You could upgrade this step using the template
module.
Fourthly, you need to set up Apache configuration for the specific virtual host using the ansible.builtin.template
module.
Fifty, you need to enable a new site using the a2ensite
via the ansible.builtin.command
module.
Sixty, you need to start the apache2
service and enable it on boot and all the dependant using the ansible.builtin.service
Ansible module.
Seventy you need to open the relevant firewall service-related ports using the community.general.ufw
Ansible module.
The Best Resources For Ansible
Video Course
Printed Book
eBooks
- Ansible by Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps
- Ansible For Windows By Examples: 50+ Automation Examples For Windows System Administrator And DevOps
- Ansible For Linux by Examples: 100+ Automation Examples For Linux System Administrator and DevOps
- Ansible Linux Filesystem By Examples: 40+ Automation Examples on Linux File and Directory Operation for Modern IT Infrastructure
- Ansible For Containers and Kubernetes By Examples: 20+ Automation Examples To Automate Containers, Kubernetes and OpenShift
- Ansible For Security by Examples: 100+ Automation Examples to Automate Security and Verify Compliance for IT Modern Infrastructure
- Ansible Tips and Tricks: 10+ Ansible Examples to Save Time and Automate More Tasks
- Ansible Linux Users & Groups By Examples: 20+ Automation Examples on Linux Users and Groups Operation for Modern IT Infrastructure
- Ansible For PostgreSQL by Examples: 10+ Examples To Automate Your PostgreSQL database
- Ansible For Amazon Web Services AWS By Examples: 10+ Examples To Automate Your AWS Modern Infrastructure
- Ansible Automation Platform By Example: A step-by-step guide for the most common user scenarios
demo
How to automate the deployment of a web server apache httpd virtual host on Debian-like systems with Ansible Playbook.
code
- httpd_debian_vhost.yml
---
- name: setup webserver vhost
hosts: all
become: true
vars:
app_user: "www-data"
http_host: "example.com"
http_conf: "example.com.conf"
http_port: "80"
disable_default: true
tasks:
- name: apache installed
ansible.builtin.apt:
name: apache2
update_cache: true
state: latest
- name: document root exist
ansible.builtin.file:
path: "/var/www/{{ http_host }}"
state: directory
owner: "{{ app_user }}"
mode: '0755'
- name: custom index.html
ansible.builtin.copy:
dest: "/var/www/{{ http_host }}/index.html"
content: |
Custom Web Page
- name: set up Apache virtualhost
ansible.builtin.template:
src: "templates/apache.conf.j2"
dest: "/etc/apache2/sites-available/{{ http_conf }}"
- name: enable new site
ansible.builtin.command: "/usr/sbin/a2ensite {{ http_conf }}"
notify: reload Apache
- name: disable default Apache site
ansible.builtin.command: "/usr/sbin/a2dissite 000-default.conf"
when: disable_default
notify: reload Apache
- name: open firewall
community.general.ufw:
rule: allow
port: "{{ http_port }}"
proto: tcp
handlers:
- name: reload Apache
ansible.builtin.service:
name: apache2
state: reloaded
- apache.conf.j2
<VirtualHost *:{{ http_port }}>
ServerAdmin webmaster@localhost
ServerName {{ http_host }}
ServerAlias www.{{ http_host }}
ErrorLog ${APACHE_LOG}/error.log
CustomLog ${APACHE_LOG}/access.log combined
DocumentRoot "/var/www/{{ http_host }}"
</VirtualHost>
execution
ansible-pilot $ ansible-playbook -i virtualmachines/ubuntu/inventory services/httpd_debian_vhost.yml
PLAY [setup webserver] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ubuntu.example.com]
TASK [apache installed] ***************************************************************************
changed: [ubuntu.example.com]
TASK [document root exist] ************************************************************************
changed: [ubuntu.example.com]
TASK [custom index.html] **************************************************************************
changed: [ubuntu.example.com]
TASK [set up Apache virtualhost] ******************************************************************
changed: [ubuntu.example.com]
TASK [enable new site] ****************************************************************************
changed: [ubuntu.example.com]
TASK [disable default Apache site] ****************************************************************
changed: [ubuntu.example.com]
TASK [open firewall] ******************************************************************************
ok: [ubuntu.example.com]
RUNNING HANDLER [reload Apache] *******************************************************************
changed: [ubuntu.example.com]
PLAY RECAP ****************************************************************************************
ubuntu.example.com : ok=9 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-pilot $
(almost) idempotency
ansible-pilot $ ansible-playbook -i virtualmachines/ubuntu/inventory services/httpd_debian_vhost.yml
PLAY [setup webserver] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ubuntu.example.com]
TASK [apache installed] ***************************************************************************
ok: [ubuntu.example.com]
TASK [document root exist] ************************************************************************
ok: [ubuntu.example.com]
TASK [custom index.html] **************************************************************************
ok: [ubuntu.example.com]
TASK [set up Apache virtualhost] ******************************************************************
ok: [ubuntu.example.com]
TASK [enable new site] ****************************************************************************
changed: [ubuntu.example.com]
TASK [disable default Apache site] ****************************************************************
changed: [ubuntu.example.com]
TASK [open firewall] ******************************************************************************
ok: [ubuntu.example.com]
RUNNING HANDLER [reload Apache] *******************************************************************
changed: [ubuntu.example.com]
PLAY RECAP ****************************************************************************************
ubuntu.example.com : ok=9 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-pilot $
before execution
ansible-pilot $ ssh [email protected]
Last login: Thu Feb 24 11:21:41 2022 from 192.168.76.111
$ sudo su
root@ubuntu:/home/devops# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
root@ubuntu:/home/devops# apt list apache2
Listing... Done
apache2/focal-updates,focal-security 2.4.41-4ubuntu3.9 amd64
apache2/focal-updates,focal-security 2.4.41-4ubuntu3.9 i386
root@ubuntu:/home/devops# apt list apache2 --installed
Listing... Done
root@ubuntu:/home/devops# dpkg -l | grep apache
root@ubuntu:/home/devops# cat /etc/apache2/sites-available/example.com.conf
cat: /etc/apache2/sites-available/example.com.conf: No such file or directory
root@ubuntu:/home/devops# ls -al /var/www/example.com
ls: cannot access '/var/www/example.com': No such file or directory
root@ubuntu:/home/devops# cat /var/www/example.com/index.html
cat: /var/www/example.com/index.html: No such file or directory
root@ubuntu:/home/devops# systemctl status apache2
Unit apache2.service could not be found.
root@ubuntu:/home/devops# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
80/tcp ALLOW IN Anywhere
22/tcp (OpenSSH) ALLOW IN Anywhere
80/tcp (v6) ALLOW IN Anywhere (v6)
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
root@ubuntu:/home/devops#
after execution
ansible-pilot $ ssh [email protected]
Last login: Thu Feb 24 11:37:49 2022 from 192.168.76.111
$ sudo su
root@ubuntu:/home/devops# apt list apache2
Listing... Done
apache2/focal-updates,focal-security,now 2.4.41-4ubuntu3.9 amd64 [installed]
apache2/focal-updates,focal-security 2.4.41-4ubuntu3.9 i386
root@ubuntu:/home/devops# apt list apache2 --installed
Listing... Done
apache2/focal-updates,focal-security,now 2.4.41-4ubuntu3.9 amd64 [installed]
N: There is 1 additional version. Please use the '-a' switch to see it
root@ubuntu:/home/devops# apt list apache2 --installed -a
Listing... Done
apache2/focal-updates,focal-security,now 2.4.41-4ubuntu3.9 amd64 [installed]
apache2/focal 2.4.41-4ubuntu3 amd64
root@ubuntu:/home/devops# dpkg -l | grep apache2
ii apache2 2.4.41-4ubuntu3.9 amd64 Apache HTTP Server
ii apache2-bin 2.4.41-4ubuntu3.9 amd64 Apache HTTP Server (modules and other binary files)
ii apache2-data 2.4.41-4ubuntu3.9 all Apache HTTP Server (common files)
ii apache2-utils 2.4.41-4ubuntu3.9 amd64 Apache HTTP Server (utility programs for web servers)
root@ubuntu:/home/devops# cat /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
DocumentRoot "/var/www/www.example.com"
</VirtualHost>root@ubuntu:/home/devops# ls -al /var/www/example.com/
total 12
drwxr-xr-x 2 www-data root 4096 Feb 24 11:37 .
drwxr-xr-x 4 root root 4096 Feb 24 11:37 ..
-rw-r--r-- 1 root root 16 Feb 24 11:37 index.html
root@ubuntu:/home/devops# cat /var/www/example.com/index.html
Custom Web Page
root@ubuntu:/home/devops# systemctl status apache2
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-02-24 11:37:04 UTC; 2min 41s ago
Docs: https://httpd.apache.org/docs/2.4/
Process: 4191 ExecReload=/usr/sbin/apachectl graceful (code=exited, status=0/SUCCESS)
Main PID: 2318 (apache2)
Tasks: 55 (limit: 1071)
Memory: 5.7M
CGroup: /system.slice/apache2.service
├─2318 /usr/sbin/apache2 -k start
├─4195 /usr/sbin/apache2 -k start
└─4228 /usr/sbin/apache2 -k start
Feb 24 11:37:04 ubuntu systemd[1]: Starting The Apache HTTP Server...
Feb 24 11:37:04 ubuntu systemd[1]: Started The Apache HTTP Server.
Feb 24 11:37:12 ubuntu systemd[1]: Reloading The Apache HTTP Server.
Feb 24 11:37:13 ubuntu systemd[1]: Reloaded The Apache HTTP Server.
Feb 24 11:37:50 ubuntu systemd[1]: Reloading The Apache HTTP Server.
Feb 24 11:37:50 ubuntu systemd[1]: Reloaded The Apache HTTP Server.
root@ubuntu:/home/devops# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
80/tcp ALLOW IN Anywhere
22/tcp (OpenSSH) ALLOW IN Anywhere
80/tcp (v6) ALLOW IN Anywhere (v6)
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
root@ubuntu:/home/devops# cat /var/log/apache2/access.log
192.168.76.111 - - [24/Feb/2022:11:41:21 +0000] "GET / HTTP/1.1" 200 299 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
192.168.76.111 - - [24/Feb/2022:11:41:23 +0000] "GET / HTTP/1.1" 200 299 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
192.168.76.111 - - [24/Feb/2022:11:41:23 +0000] "GET /favicon.ico HTTP/1.1" 404 497 "http://ubuntu.example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
192.168.76.111 - - [24/Feb/2022:11:41:23 +0000] "GET /apple-touch-icon-precomposed.png HTTP/1.1" 404 497 "-" "Safari/16612.2.9.1.30 CFNetwork/1240.0.4 Darwin/20.6.0"
192.168.76.111 - - [24/Feb/2022:11:41:23 +0000] "GET /apple-touch-icon.png HTTP/1.1" 404 496 "-" "Safari/16612.2.9.1.30 CFNetwork/1240.0.4 Darwin/20.6.0"
192.168.76.111 - - [24/Feb/2022:11:41:49 +0000] "GET / HTTP/1.1" 200 299 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
192.168.76.111 - - [24/Feb/2022:11:41:49 +0000] "GET /favicon.ico HTTP/1.1" 404 492 "http://192.168.76.32/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
192.168.76.111 - - [24/Feb/2022:11:42:14 +0000] "-" 408 0 "-" "-"
192.168.76.111 - - [24/Feb/2022:11:42:17 +0000] "GET / HTTP/1.1" 304 180 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
root@ubuntu:/home/devops#
Recap
Now you know how to deploy a webserver apache httpd virtual host on Debian-like systems with Ansible. Subscribe to the YouTube channel, Medium, Website, Twitter, and Substack to not miss the next episode of the Ansible Pilot.
Academy
Learn the Ansible automation technology with some real-life examples in my
My book Ansible By Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps
Donate
Want to keep this project going? Please donate