Use this file to discover all available pages before exploring further.
Podman is a daemonless, rootless-by-default alternative to Docker. Timeful’s hardened container images work seamlessly with Podman — the same fixed UIDs (backend 1000, frontend 101, MongoDB 999) are automatically remapped into your user’s subUID range, so volume permissions just work. You can start quickly with podman-compose, or graduate to Podman Quadlets for full systemd integration with dependency ordering, auto-restart, and centralised journal logging.
Timeful requires Podman 4.4 or later for Quadlet support. For Podman Compose only, any recent Podman release is sufficient.
Podman Compose reads the same docker-compose.yml files as Docker Compose, so you can drop in a replacement with zero config changes.
# Install podman-compose (requires Python 3)pip3 install podman-compose# Start the full stack (MongoDB + backend + frontend)podman-compose up -d# Follow logspodman-compose logs -f# Stop the stackpodman-compose down
To use the pre-built GHCR images instead of building from source:
podman-compose -f docker-compose.ghcr.yml up -d
All security hardening — non-root users, no-new-privileges, capability dropping — is declared in the Compose files and takes effect automatically with Podman.
Quadlets are .container, .network, and .volume unit files that Podman translates into native systemd service units at daemon-reload time. This gives you standard systemctl management, boot-time startup, and journald logging for every Timeful container.
[Unit]Description=Timeful MongoDB DatabaseAfter=network-online.targetWants=network-online.target[Container]Image=docker.io/library/mongo:6.0ContainerName=timeful-mongodbAutoUpdate=registryUser=999:999Environment=MONGO_INITDB_DATABASE=schej-itNetwork=timeful.network# Remove PublishPort in production to keep MongoDB off the host networkPublishPort=27017:27017Volume=timeful-mongodb-data:/data/dbVolume=timeful-mongodb-config:/data/configdbSecurityLabelDisable=falseNoNewPrivileges=trueHealthCmd=mongosh --eval "db.adminCommand('ping')" --quietHealthInterval=10sHealthTimeout=5sHealthRetries=5[Service]Restart=unless-stoppedTimeoutStartSec=900[Install]WantedBy=default.target
[Unit]Description=Timeful Backend API ServerAfter=timeful-mongodb.serviceRequires=timeful-mongodb.serviceAfter=network-online.targetWants=network-online.target[Container]Image=localhost/timeful-backend:latestContainerName=timeful-backendAutoUpdate=registryUser=1000:1000Environment=MONGO_URI=mongodb://timeful-mongodb:27017Environment=MONGO_DB_NAME=schej-itEnvironment=GIN_MODE=releaseEnvironmentFile=%h/.config/timeful/backend.envNetwork=timeful.networkVolume=timeful-backend-logs:/app/logsSecurityLabelDisable=falseNoNewPrivileges=true[Service]Restart=unless-stoppedTimeoutStartSec=900[Install]WantedBy=default.target
[Unit]Description=Timeful Frontend Web ServerAfter=timeful-backend.serviceRequires=timeful-backend.serviceAfter=network-online.targetWants=network-online.target[Container]Image=localhost/timeful-frontend:latestContainerName=timeful-frontendAutoUpdate=registryUser=101:101Network=timeful.networkPublishPort=3002:80Environment=BACKEND_HOST=timeful-backendEnvironment=BACKEND_PORT=3002SecurityLabelDisable=falseNoNewPrivileges=true[Service]Restart=unless-stoppedTimeoutStartSec=900[Install]WantedBy=default.target
7
Create the volume units
Named volumes referenced in the container units need matching .volume files so Podman manages them as quadlet resources.~/.config/containers/systemd/timeful-mongodb-data.volume:
When Podman runs a container as UID 1000 inside a rootless user session, that UID is mapped to an unprivileged UID in your subuid range on the host. The net effect is:
Container processes cannot affect host files owned by other users
Named volumes created by containers appear owned by your user on the host
No chown workarounds are needed — Timeful’s fixed UIDs work transparently
To verify your subuid allocation:
cat /etc/subuid # should contain a line like: youruser:100000:65536podman unshare id # shows UID 0 inside the user namespace