- A fresh Ubuntu server (local VM or remote)
- An
ansibleuser created on the server with a home directory - Your SSH public key copied to that user's
~/.ssh/authorized_keys(usessh-copy-id ansible@<ip>) - The
ansibleuser has passwordless sudo (add to/etc/sudoers.d/ansible:ansible ALL=(ALL) NOPASSWD: ALL) community.generalcollection installed locally:ansible-galaxy collection install -r requirements.yml
Each server has its own playbook (e.g. web01.whservice.nl.yml) and each domain has its own private inventory file in private/. Neither is committed to git.
Copy inventory-example.yml to private/<hostname>.yml and fill in the IP, hostname, dns_servers, and adjust ansible_ssh_private_key_file to point to the key you use to reach the ansible user.
Create <hostname>.yml in the project root. Include only the plays that apply:
---
- name: Deploy <hostname>
become: yes
hosts: webservers
roles:
- basic
- web
vars_files:
- roles/basic/vars/private/linuxusers.ymlroles/basic/vars/private/linuxusers.yml — Linux users. See the README in that directory for the structure. This file is gitignored.
roles/web/vars/private/ — Virtual hosts config (apachevhosts.yml). See the README in that directory.
roles/mail/vars/private/mailvars.yml — Mail server config. See the README in that directory.
ansible-playbook -i private/<hostname>.yml <hostname>.ymlNo password prompts — Ansible authenticates via the key in ansible_ssh_private_key_file, and become uses passwordless sudo.
| Role | What it does |
|---|---|
basic |
Hostname, base packages, unattended-upgrades, users, SSH hardening, firewall, fail2ban |
web |
OpenLiteSpeed, PHP 8.3, Let's Encrypt directory, certbot |
db |
MariaDB, phpMyAdmin |
mail |
Postfix, Dovecot, OpenDKIM, SpamAssassin, Roundcube |
Never store plaintext passwords. Generate a hash with:
mkpasswd --method=sha-512Paste the result into linuxusers.yml as the password field.
- Setup Zabbix
- Setup Z-push (90% done)
- Setup webmail subdomains