Sandbox – local testing¶
sandbox/ contains a Docker-based environment for testing playbooks against a throwaway container instead of a real server. Use it to validate changes before applying them to staging or production.
How it works¶
sandbox/init.sh does the following on every run:
- Generates a temporary ed25519 SSH key pair.
- Tears down and removes any existing container (and its local image).
- Builds and starts a fresh container with the temporary public key injected.
- Waits for SSH to be ready on
127.0.0.1:2222. - Runs
ansible-playbookagainst the container.
The container is a plain Ubuntu image with sshd and sudo configured. It mimics a fresh server.
Some roles are disabled in sandbox mode because they require external services or elevated system access that is not available in a container: - auditd_enabled: false - cadvisor_enabled: false - cloudflare_enabled: false - sqlbak_agent_enabled: false
First-time setup¶
The sandbox defaults to the local inventory, and inventories/local/ is gitignored on purpose - it is yours, not the repo's. Create it once from the tracked example:
The example puts the container in both web and db, so web.yml, db.yml and site.yml all have a host to run against. init.sh overrides the connection variables at run time, so you only need to edit the file if you run ansible-playbook against that inventory yourself.
Basic usage¶
Run the default playbook (site.yml) against the local container:
Examples¶
Run a specific playbook¶
Dry run (check mode)¶
init.sh rebuilds the container from scratch on every run, and a dry run against a bare host fails almost immediately: check mode only simulates the package installs, so the first task depending on one of them errors out (common_baseline : Set timezone fails because tzdata was never really installed). Converge the container first, then dry-run against the running container directly:
bash sandbox/init.sh --playbook web.yml
ansible-playbook -i inventories/local/hosts.yml web.yml --check --diff
Run a single role¶
Use a real inventory (prod/stage) instead of local¶
Useful when you need real host variables (e.g. to test Cloudflare config):
Note: Ansible will still connect to the local container – the inventory is used only to load variables for the specified host.
Run a specific playbook against a real inventory host¶
Limitations¶
--roleand--playbookcannot be combined (the script will error).--rolegenerates a one-task playbook in the repo root (.sandbox-role-<name>.yml) and removes it on exit. It has to live there:playbook_dirdecides whichgroup_vars/Ansible loads and where roles resolvekeys/*.pubfrom, so a playbook outside the repo silently losesgroup_vars/all/.- The container does not persist between runs – every
init.shcall starts fresh. It does keep running afterwards, so you can pointansible-playbookatinventories/local/hosts.ymlyourself for repeat or--checkruns. - Roles that call external APIs (Cloudflare) will still make real API calls if not disabled.
Docker files¶
sandbox/
├── init.sh # Entry point
├── hosts.local.yml.example # Copy to inventories/local/hosts.yml
└── docker/
├── Dockerfile # Ubuntu image with sshd + sudo
└── docker-compose.yml # Exposes port 2222 → 22
The SSH port is fixed at 2222 on the host. Make sure nothing else is bound to that port before running.