Tool to install and configure web servers from scratch using Ansible.
Tested with Debian 13 on OVHCloud VPS and KS3 server.
- The server must have a user account with sudo rights. (Ensure the user account has a password as well as the root account.) To configure the passwords you can:
- Run
sudo passwd {{user}} - Run
sudo su -thenpasswd
- Run
- Add your SSH public key to enable passwordless SSH login for the user account. (
ssh-copy-id -i ~/.ssh/id_rsa {{user}}@{{hostname}}) - On localhost, please install the required Ansible mobules
ansible-galaxy collection install -r requirements.yml
- Create a
hosts/{{domain}}directory where{{domain}}is the domain name for the server to configure. - Copy hosts.yml.dist into
./hosts/{{domain}}/hosts.yml. - Copy hosts.init.yml.dist into
hosts/{{domain}}/hosts.init.yml. - For both
hosts.ymlandhosts.init.ymlfiles:- Define your server IPs in the hosts list.
- Replace the following variables:
#hostname: Desired domain name for the server to configure.#user: username of the user account.#ssh_port: Desired SSH port to use instead of the default 22 port.#admin_email: Email address to receive admin notifications.
This script is made of the two following parts:
The init phase:
- Changes the SSH port
- Disables SSH password authentication
- Disables SSH root login
Run ansible-playbook -i hosts/{{domain}}/hosts.init.yml playbook.init.yml to run this phase.
The install phase:
- Configures hostname (Optional, see Manage hostname)
- Installs and configures Postfix
- Installs and configures OpenDKIM (Optional, see Optional DKIM support)
Optionally sets up weekly security updates(not tested on Debian 12 and 13)- Installs and configures fail2ban. Also installs firewalld as firewall.
If you experience with host being unreachable after reboot, you can defer firewalld startup by setting(Should not be necessary anymore as fail2ban and docker are now started after firewalld.)defer_firewalldtotrue. - Installs PortSentry to prevent port scanning. PortSentry does not block the IP address of the scanner, but it will log the event to let fail2ban handle the banning.
- Installs Docker and Docker Compose
- Installs Caddy as a reverse proxy
- Installs and configure a monitoring interface
- cAdvisor is installed to collect Docker containers monitoring data
- Promotheus is installed to store cAdvisor data
- Grafana Alloy is installed to collect Docker and journald logs into Loki
- Grafana is installed as monitoring dashboard with prepopulated items and is exposed through HTTPS using the
statussubdomain (defined in roles/monitoring/defaults/main.yml) - Optional DKIM support (see Optional DKIM support)
- Installs a private Docker registry (optional) (See Install a private Docker registry)
- Installs WordPress website(s) (optional) (See Install an additional WordPress website)
This phase connects to the server(s) as the specified user and uses your personal SSH key.
Run ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml to run this phase.
- Update DKIM DNS records (if enabled, see Optional DKIM support)
- Log in to Grafana with
admin/admincredentials and change the password.- Test the SMTP configuration by sending a test email from the
Admin emailcontact point.
- Test the SMTP configuration by sending a test email from the
You're all set!
Ensure fail2ban and portsentry are compatible with Debian 13. (Not tested yet)
sudo journalctl -u portsentry -fto check if portsentry is running properly.sudo fail2ban-regex "systemd-journal" /etc/fail2ban/filter.d/portsentry.confto check if fail2ban is properly configured to ban portsentry events.sudo fail2ban-client statusandsudo fail2ban-client status portsentryto check the status of fail2ban and the portsentry jail.sudo firewall-cmd --direct --get-all-rulesto check if fail2ban is well synchronized with firewalld and is able to ban IP addresses.
In case of accidental ban, run sudo fail2ban-client unban YOUR_IP_ADDRESS --jail portsentry to unban your IP address.
And remember to purge /etc/hosts.deny files if they contain hundreds of IP addresses. You can run sudo echo "" > /etc/hosts.deny.
The following operations have not been tested on Debian 12 and 13.
- Installs OpenVPN (optional)
- Configures 2 OpenVPN servers with 2FA enabled
- a first instance is configured on port 1194 using UDP
- a fallback instance is configured on port 80 using TCP (Enables port-sharing to redirect usual HTTP requests to port 8080)
- Generates client certificate(s) and downloads the configuration file(s) (See Install an additional VPN client certificate)
- Generates QRCode URLs to configure 2FA for each client
- Configures 2 OpenVPN servers with 2FA enabled
- Installs Samba (optional)
- Configure one or multiple shares
- Exposes a Samba server into a Docker container with a fixed IP address (172.21.0.4 by default), only reachable from the VPN subnet
- Installs Nextcloud (optional)
- Creates admin user account
- Preconfigures SMTP server
- See below for additional manual operations
- Installs PrestaShop website(s) (optional) (See Install an additional PrestaShop website)
The hostname of the server can be managed by defining the manage_hostname variable in hosts.yml. This setting can conflict if the hostname is set from the cloud provider, so it is disabled by default.
DKIM support can be enabled by defining the enable_dkim variable to true in hosts.yml.
To ensure deliverability of emails sent to the same domain than the server hostname, enabling DKIM requires a hubbed_hosts file located at hosts/{{domain}}/hubbed_hosts. If no mails are sent to the server hostname, you can leave this file empty.
This script creates two separate DKIM configurations:
- The first one uses the
serverselector, and is used for system emails like cron tasks. - The second one uses the
statusselector, and is used for grafana alerts.
The scripts generate the RSA keys on the server side, and retrieves the details of the DNS records to configure in a {{selector}}._domainkey.txt file located in the hosts/{{domain}}/ directory, where {{selector}} corresponds to the respective DKIM selector.
Auto updates for reverse proxy and monitoring containers can be enabled be defining the auto_update variable to true in hosts.yml.
If auto updates are disabled, these containers can be updated manually by running ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml --tags=caddy --tags=monitoring.
In hosts.yml, set the following variables:
install_docker_registrytotruedocker_registry_usernamedocker_registry_password_hash
You can generate a bcrypt hash by running htpasswd -nbBC 10 {{docker_registry_username}} {{docker_registry_password}}.
Then, run ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml --tags=docker-registry to deploy the registry.
In hosts.yml, add an item in the wordpress_websites list, and set:
hostname(The website will be installed in/var/www/hostname/)name(Impacts the composer project name and the db name)db_userdb_passworddb_root_password
Then, run ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml --tags=wordpress to deploy the new website.
Default versions for WordPress and MariaDB can be set in roles/wordpress/defaults/main.yml. Please verify compatibility before upgrading. WordPress is installed with automatic updates enabled, so the default version is only used for the initial install.
The following features have not been tested with Debian 11, 12, and 13
NextCloud install requires few additional operations to be fully optimized.
Go to /settings/admin/overview, and if it is suggested to run occ db:add-missing-indices and occ db:convert-filecache-bigint then run:
docker-compose exec -u www-data nextcloud php occ db:add-missing-indicesdocker-compose exec -u www-data nextcloud php occ db:convert-filecache-bigint
In hosts.yml, add an item in the vpn_client_certificates list, and set:
namepassphrase
Then, run ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml --tags=openvpn to create the new certificate.
In hosts.yml, add an item in the prestashop_websites list, and set:
hostname(The website will be installed in/var/www/hostname/)name(Impacts the composer project name and the db name)db_userdb_passworddb_root_passwordadmin_password(Used to access the back office. The login is the value ofadmin_email)
Then, run ansible-playbook -i hosts/{{domain}}/hosts.yml playbook.yml --tags=prestashop to deploy the new website.