7.0 KiB
Ansible
Two playbooks families live here: one for updating Docker Compose stacks day-to-day, and one for provisioning a fresh server from scratch. Both are designed to run through Semaphore.
Directory structure
ansible/
├── update-playbooks/ # day-to-day Docker Compose stack updates
│ ├── roles/
│ │ └── docker_compose_update/
│ │ ├── defaults/ # role defaults (currently empty — vars come from playbooks)
│ │ └── tasks/
│ │ └── main.yml # shared pull → recreate → health-check → prune logic
│ ├── ping.yml # connectivity check
│ ├── update-agent-canvas.yml
│ ├── update-filebrowser.yml
│ ├── update-grafana.yml
│ ├── update-n8n.yml
│ ├── update-npm.yml
│ ├── update-pihole.yml
│ ├── update-plex.yml
│ ├── update-portainer.yml
│ ├── update-prowlarr.yml
│ ├── update-radarr.yml
│ ├── update-samba.yml
│ ├── update-sentinel.yml
│ ├── update-smartctl.yml
│ ├── update-sonarr.yml
│ └── update-whisparr.yml
│
└── preparing-playbook/ # one-time server provisioning (learning lab / Vagrant testing)
└── reza-ansible/
├── ansible.cfg
├── preparing.yaml # playbook: harden + Docker install
├── repo-server.yaml # playbook: Nexus + Traefik repo server
├── .vault.pass # ⚠️ gitignored — must be created locally
├── inventory/
│ ├── host.yaml # inventory — hosts and connection details
│ ├── group_vars/
│ │ └── all/
│ │ ├── preparing.yaml # packages, sshd, iptables, sysctl, auditd
│ │ ├── docker.yaml # Docker daemon config (proxy, registry mirror)
│ │ ├── proxy.yaml # HTTP proxy credentials
│ │ ├── repo-server.yaml # Nexus/Traefik/domain config
│ │ ├── backup.yaml # backup volume/path config
│ │ └── vault.yaml # 🔒 Ansible Vault encrypted secrets
│ └── host_vars/
│ └── vagrant-test.yaml # Vagrant-specific overrides
└── roles/
├── preparing_server/ # packages, sshd, iptables, fail2ban, auditd, lynis, sysctl
├── docker/ # Docker CE + Compose plugin install + daemon config
└── repo-server/ # Nexus3 + Traefik + Docker registry setup
update-playbooks — how it works
Every always-on Docker Compose stack has a thin playbook that delegates to the shared docker_compose_update role. Each playbook is intentionally minimal:
---
- hosts: all
become: true
vars:
compose_dir: "/home/rezayaghobi/docker/<service>"
service_name: "<Friendly Name>"
roles:
- docker_compose_update
The shared role runs the same sequence for every stack:
- Records start time for a duration summary
- Snapshots
docker system dfbefore the pull - Runs
docker compose pullin the stack's directory - Snapshots
docker system dfagain — before/after comparison catches unexpectedly large pulls - Runs
docker compose up -dto recreate containers with new images - Waits 5 seconds, then checks
docker compose ps - Asserts every container is up — fails the run (and skips cleanup) if anything shows
Exit,Created, orRestarting - Runs
docker image prune -fto reclaim space — only if the health check passed - Prints a summary — duration, cleanup result, pass/fail
If any step fails, Ansible stops immediately rather than continuing to prune images out from under a broken deployment.
Adding a new stack
Copy any existing playbook, change compose_dir and service_name. No changes to the role itself.
preparing-playbook — server provisioning
A full server preparation pipeline for fresh Ubuntu/Debian installs, designed for testing against the Vagrant VM before running on real hardware.
preparing.yaml
Runs two roles in sequence:
preparing_server— installs base packages, configures sshd, sets up iptables rules, enables fail2ban, configures auditd, tunes sysctl, installs lynis for security auditingdocker— installs Docker CE + Compose plugin from Docker's official repo, configures daemon with proxy/mirror settings
repo-server.yaml
Runs the repo-server role — pulls Nexus3, Traefik, and supporting images, then configures and starts them as a self-hosted artifact repository with TLS via Traefik.
Inventory
inventory/host.yaml— defines therepo-serversgroup (currently justvagrant-teston127.0.0.1:2222)inventory/group_vars/all/— shared variables for all hostsinventory/host_vars/vagrant-test.yaml— Vagrant-specific overrides (sshd port, iptables trusted ranges)
Running locally against Vagrant
cd ansible/preparing-playbook/reza-ansible
# provision the server (packages + hardening + Docker)
ansible-playbook preparing.yaml -i inventory/host.yaml
# set up the repo server (Nexus + Traefik)
ansible-playbook repo-server.yaml -i inventory/host.yaml
# connectivity check only
ansible all -m ping -i inventory/host.yaml
⚠️ Before pushing to GitHub
The preparing-playbook/ contains hardcoded credentials and personal infrastructure details that must be sanitized before making this repo public:
| File | Contains | What to do |
|---|---|---|
.vault.pass |
Plaintext vault password | Already in .gitignore — must be recreated locally |
group_vars/all/vault.yaml |
Encrypted secrets | Safe to commit (Ansible Vault encrypted) |
group_vars/all/proxy.yaml |
Proxy username + password in plaintext | Move credentials to vault.yaml, reference as {{ vault_proxy_user }}:{{ vault_proxy_pass }} |
group_vars/all/docker.yaml |
Proxy IP, registry mirror URL | Replace with {{ vault_docker_proxy }} / {{ vault_registry_mirror }} |
group_vars/all/preparing.yaml |
Trusted IP addresses | Replace with {{ vault_trusted_ips }} |
group_vars/all/repo-server.yaml |
Domain name, emails, usernames | Replace with vault variables |
inventory/host.yaml |
SSH key file path | Replace with {{ vault_ssh_key_path }} or a generic path |
The update-playbooks/ are safe as-is — they only contain compose_dir paths with the local username and service_name labels.
Running via Semaphore
Both playbook families are executed through Semaphore — no manual ansible-playbook on the server. Each playbook is set up as a Task Template pointed at this repo. See Ansible Playbooks for the full workflow and the Semaphore Guide for setup details.