Managing Personal Servers with Docker Compose as IaC
Hands-on experience shows that handling 1–5 servers with docker compose up streamlines Infrastructure as Code (IaC) without needing Ansible. This approach requires familiarity with docker-compose.yml, manual setup experience, and a drive to automate. Key pieces include a script for rsync + SSH deployment and storing secrets in Git (using sops or git-secret). Docker Compose delivers idempotency, cutting down on manual work.
The repo with the template showcases DNS/email (primary/secondary), VPN (WireGuard), nftables firewall, and Netdata monitoring with Telegram alerts. It includes docs and infrastructure diagrams.
Initial Setup and Bootstrap
For a new server, create bootstrap.sh—a minimal script to install Docker. Run it manually or via cloud-init. The example keeps changes to a minimum: OS upgrades (like do-release-upgrade) or console tools (nvim config) need occasional tweaks.
# Simplified bootstrap.sh snippet
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker compose version
The script skips redundant manual steps, zeroing in on Docker.
Idempotent Firewall with nftables
Docker tweaks nftables rules in the ip/ip6 families. Store yours in inet to dodge conflicts. bootstrap.sh sets up /etc/nftables.conf, and the container applies migrate.nft on up.
Use dockerize to render and inject variables (like IP from host-facts.sh). Full idempotency:
flush ruleset inet
flush chain ip filter DOCKER-USER
flush chain ip6 filter DOCKER-USER
table inet filter {
chain prerouting-before-docker {
type filter hook prerouting priority dstnat - 2;
# Filtering before Docker NAT
}
}
Handling WireGuard and System Emails
WireGuard spins up a kernel interface: tear it down when stopping the container.
finish() {
wg-quick down "$INTERFACE"
}
trap finish EXIT
wg-quick up "$INTERFACE"
sleep inf
System emails (from systemd, unattended-upgrades) route through a bind-mount of /var/spool/postfix into a postfix container. Shared queue between host and container.
host-facts.sh for Server-Specific Details
This script generates env vars for docker-compose.yml: IP, UID/GID, interfaces.
uid() { getent passwd "$1" | cut -d: -f3; }
gid() { getent group "$1" | cut -d: -f3; }
metadata() { curl -s "http://169.254.169.254/metadata/v1$1"; }
set -euo pipefail
if test -z "${DEVEL:-}"; then
WAN_IP="$(metadata /interfaces/public/0/ipv4/address)"
UID_POSTFIX="$(uid postfix)"
GID_POSTFIX="$(gid postfix)"
else
WAN_IP=0.0.0.0
UID_POSTFIX=1001
GID_POSTFIX=1001
fi
for _var in WAN_IP UID_POSTFIX GID_POSTFIX; do eval "export $_var=\"$${_var}\" "; done
— Editorial Team
No comments yet.