Domain 2 capstone -- run a service the way it should be run
Task
Bring the Services and User Management domain together: a containerised application with persistent data, its own network, least privilege, a dedicated service account on the host, a systemd unit that manages it, and a scheduled backup. Prove each property rather than assuming it.
Steps
-
Least-privilege image. Build the app image with a non-root
USER, a pinned base tag, and dependencies installed before source is copied. Prove the container runs as a non-root user. -
Data and network. Run the app (container
app) and its database (containerdb,postgres:16, database userapp, data in a tableitems) on a user-defined network with a named volume for the data, the database reachable by name, and the published port 8080 bound to loopback behind a reverse proxy. -
Host account. Create a dedicated service account
svc-appwithnologinand no password to own the deployment, and confirm it cannot be logged into. -
Rootless. Run the whole thing rootless, as
svc-app, and explain what a container escape yields under rootless versus under a root daemon. With anologinshell, act as the account without a login:sudo -u svc-app XDG_RUNTIME_DIR=/run/user/$(id -u svc-app) podman .... Its containers, volumes and units are its own: root'spodman psandsystemctlwill not show them. -
systemd management. As
svc-app, write a systemd USER unit that runs the container --podman generate systemd --new --name app --filesgivescontainer-app.service(Quadlet'sapp.containergivesapp.serviceinstead; use that name below if you go that way) -- install it in~svc-app/.config/systemd/user/,systemctl --user enable --nowit, and runloginctl enable-linger svc-app. Without lingering a user's units start when that user logs in, not at boot -- andsvc-appnever logs in. Prove it starts on boot -- the enable-versus-start distinction -- and restarts on failure. - Backup. Add a systemd timer that dumps the database with the application's own tool (not a tar of the live data directory) to a location the container cannot write to, and prove a restore works.
- Prove persistence. Restart the host, and confirm the service comes back and the data is intact.
- Write an evidence summary tying each property to the command that proves it.
Verify
as() { sudo -u svc-app XDG_RUNTIME_DIR="/run/user/$(id -u svc-app)" "$@"; }
as podman inspect app --format '{{.Config.User}}' # non-root
as podman exec app getent hosts db # resolves by name
getent passwd svc-app | cut -d: -f7 # nologin
loginctl show-user svc-app -p Linger # Linger=yes
as systemctl --user is-enabled container-app # enabled
ss -tlnp | grep ':8080' # 127.0.0.1 only
# after a host reboot:
as systemctl --user is-active container-app && \
as podman exec db psql -U app -tAc 'select count(*) from items' # running, data intact
Every container and unit check runs AS svc-app: a rootless deployment is invisible to root's podman and to the system systemctl, so the same commands run as root report nothing or "not found" for a correct setup. psql needs -U app: without it, it connects as the container's user, root, which has no role in the database.
The capstone passes when the service survives a reboot with its data, runs as a non-root user rootless, is reachable only where intended, and has a restore you have actually performed. A backup you have not restored does not count, and a unit you started but did not enable will not survive the reboot that tests it.
Notes
The domain's recurring lessons all appear here: data outside a volume is lost; enable is not start; a service account should not be able to log in; rootless turns an escape into an unprivileged user; and a backup is a hypothesis until restored. The capstone is where they stop being separate facts and become one running system.
This is an independent study companion for CompTIA Linux+ and is not produced by or endorsed by CompTIA.