The first run of this playbook against emily failed, and all three reasons are worth recording because none of them is visible from the files alone. keep-id runs the container *process* as the uid it maps, so UserNS=keep-id:uid=1000,gid=1000 started the container as `node` rather than root -- and the image's start-docker.sh is written to start as root, ending with `exec su -c "node ./main.mjs" node`. `su` from a non-root uid asks for a password the node account does not have, so the container died with "su: Authentication failure" and Restart=always turned that into a crash loop. User=0 forces the process back to root and changes nothing about the mapping: node is still tanshu, so the app is still what writes the data directory and it stays tanshu's. nextcloud.container carries the same pair for the same reason. The health check had to be defined rather than inherited. `latest` floated between the two pulls -- monoco runs v0.95.0 (built 2025-06-15) and emily pulled v0.106.0 (built 2026-09-25) -- and v0.106.0 ships no HEALTHCHECK where v0.95.0 had docker_healthcheck.cjs, so `podman healthcheck run` failed with "no defined healthcheck". The unit now defines HealthCmd= against /api/health-check, the endpoint the app advertises itself in the healthcheck-url file it writes into the data directory, and the image has curl for it (but not wget). The verify and health-check steps are folded in with the start, because they fail the same way and want the same evidence. `systemctl start` reports success as soon as systemd holds the unit, so a container that dies a second later surfaced only as "activating" from `is-active`; the reason -- that "su: Authentication failure" -- was in the journal and nowhere the play looked. The rescue now dumps status, journal and podman logs for any of the three, which is how the su failure was found in the first place. The version float becomes a documented risk rather than a hypothetical: the cutover upgrades the app as well as the runtime, and the first start migrates document.db forward. That is one-way for the copy on emily, so the readme compares digests before the first start, keeps monoco's own tree as the rollback, and notes that pinning a tag here would decouple the two changes. Verified on emily rather than by inspection: the rendered unit was run first as a throwaway Quadlet unit against a scratch volume, then the playbook itself ran to ok=22/changed=6/failed=0 with the mount assertion passing, `healthcheck run` returning 0, podman reporting healthy, and the app's files landing in ~/podman-data/trilium owned by tanshu. The data directory was emptied afterwards and the unit left stopped, ready for the copy from monoco, which has not run yet.
Ansible Playbooks
A collection of minimalist Ansible playbooks for automating server setups, based on DigitalOcean's Community guides.
- Initial Server Setup for Ubuntu 18.04 *
- Apache on Ubuntu 18.04
- Postgres on Ubuntu 19.10
- Redis on Ubuntu 19.10
- Bitwarden Ubuntu 19.10
- Gitea on Ubuntu 19.10
*the Initial Server Setup should be your starting point for fresh servers.
Playbook Structure
The playbooks contained in this repository were created for educational purposes, and should serve as a base for you to create your own playbooks and roles.
Although we opt to not use roles, our playbooks follow a distinctive structure to facilitate reuse while keeping them mostly self-contained and straightforward.
For instance, this is how the lemp playbook is structured:
lemp_ubuntu1804
├── files
│ ├── info.php.j2
│ └── nginx.conf.j2
├── vars
│ └── default.yml
├── playbook.yml
└── readme.md
files/: directory containing templates and other files required by the playbook.vars/: directory to save variable files. Adefault.ymlvar file is included by default.playbook.yml: the playbook file.readme.md: instructions and links related to this playbook.
Getting Started
To set up your Ansible environment, please follow our guide on How to Install and Configure Ansible on Ubuntu 18.04.
Connection Test
From your local machine or Ansible control node, run:
ansible all -m ping -u remote_user
If you're able to get a "pong" reply back from your node(s), your setup works as expected and you'll be able to run both ad-hoc commands and playbooks on your nodes, using Ansible.
Guides
The following guides cover how to use the playbooks you'll find in this repository.
Initial Server Setup
Once you have executed the initial server setup, you can choose from any of the available server setup playbooks:
Web Servers
- Apache on Ubuntu 18.04
- LEMP (Linux, Nginx, MySQL, PHP) on Ubuntu 18.04
- LAMP (Linux, Apache, MySQL, PHP) on Ubuntu 18.04