A reusable Ansible template for preparing an Ubuntu VPS to run Docker workloads reliably. It is currently tested on OVHcloud VPS.
The repository manages the Linux host baseline only. It deliberately does not deploy applications, manage secrets, manage provider firewalls, or modify provider-managed networking and cloud-init files.
- Required system packages and OpenSSH.
- Managed OpenSSH key-only authentication hardening.
- Docker Engine, containerd, Buildx, and Docker Compose v2 from Docker's official Ubuntu repository.
- Docker live restore and bounded container logs.
- Docker
DOCKER-USERingress guard for published container ports. - Host Nginx reverse proxy for a working example application.
- A Docker Compose example application bound only to loopback.
- Daily unattended Ubuntu security updates without automatic reboots.
- Bounded journald disk usage.
- UFW host firewall with SSH and HTTP allowed and other incoming traffic denied.
- Verification of SSH, Docker, systemd services, disk space, and reboot status.
The baseline requires Ubuntu with systemd but does not enforce a specific Ubuntu release.
Use this repository as a template, then clone the project-specific copy.
The controller requires make, Python 3.12, and OpenSSH.
Install the pinned controller dependencies:
make setupCreate and edit the local inventory:
cp inventory/hosts.example.yml inventory/hosts.ymlDuring VPS installation, select the SSH public key belonging to the controller machine. Confirm key-based access and record the host key before using Ansible:
ssh ubuntu@203.0.113.10Preview, apply, and verify the baseline:
make ping
make check
make provision
make verifymake setup Install pinned dependencies into .venv
make validate Run syntax checks and ansible-lint
make ping Test Ansible connectivity
make check Preview provisioning changes
make provision Apply or repair the VPS baseline
make verify Verify the resulting server state
make maintenance Upgrade operating-system packages
Provisioning installs required packages but does not upgrade every installed
package. make maintenance performs that explicit upgrade and reports when a
reboot is required; it never reboots automatically.
All apt operations wait up to ten minutes for legitimate Ubuntu background
maintenance, such as unattended-upgrades, to release the package-manager
lock.
- Configure hosts and connection details in ignored
inventory/hosts.yml. - Configure cross-play policy and verification thresholds in
inventory/group_vars/all.yml. - Override role behavior through namespaced defaults in each
roles/<role>/defaults/main.yml.
The main provisioning sequence is:
preflight -> base -> firewall -> docker -> example_app -> nginx -> security_updates
Users are intentionally not added to the docker group because Docker access
is effectively root access. Run Docker commands with sudo.
OpenSSH is managed with key-only authentication. Password and keyboard-interactive authentication are disabled, public-key authentication is enabled, root login is limited to key-only access, and authentication attempts are capped.
UFW allows the configured Ansible SSH port from any source, denies other incoming host traffic except HTTP, allows outgoing traffic, and denies routed traffic by default. Existing UFW rules are preserved.
Docker-published container ports bypass normal UFW input rules because Docker
handles that traffic before UFW. This baseline installs a managed
DOCKER-USER guard that drops new forwarded ingress arriving on the host's
external interface before Docker's own allow rules can expose containers.
Continue to bind application containers to loopback and use the VPS provider's
network firewall as a separate defense-in-depth control.
The included example app demonstrates the intended production pattern:
internet -> host Nginx :80 -> 127.0.0.1:18080 -> Docker Compose container
The example app lives at /opt/example-app on the VPS and uses the default
page included in the container image. Its Compose service publishes
127.0.0.1:18080:8080, so the container is reachable by host Nginx but is not
directly exposed on the public network interface.