Timeful’s Docker images and Compose files are hardened with defence-in-depth security controls. All three containers run as dedicated non-root users, drop every Linux capability they do not need, and prevent privilege escalation. These measures work automatically with both standard Docker and rootless runtimes (Docker rootless mode and Podman).Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ptshen/timeful-plus/llms.txt
Use this file to discover all available pages before exploring further.
Hardening Already in Place
Non-Root Container Users
Every container runs as a dedicated low-privilege user. No service ever starts asroot:
| Container | User | UID |
|---|---|---|
backend | appuser | 1000 |
frontend | nginx | 101 |
mongodb | mongodb | 999 |
chown is required for named volumes.
Capability Dropping
Thedocker-compose.yml files drop all Linux capabilities by default (cap_drop: ALL) and add back only the minimum set required for each service:
| Container | Capabilities Added Back |
|---|---|
backend | (none — runs with zero capabilities) |
frontend | NET_BIND_SERVICE, CHOWN, SETGID, SETUID |
mongodb | CHOWN, SETGID, SETUID |
NET_BIND_SERVICE allows nginx to bind to port 80 inside the container without root. CHOWN, SETGID, and SETUID allow MongoDB and nginx to set up file ownership on startup.
No New Privileges
All containers set theno-new-privileges: true security option. This prevents any process inside the container — including setuid binaries — from gaining capabilities that were not present at container start time.
Read-Only Config Mounts
config.js (the frontend runtime configuration that carries googleClientId and microsoftClientId) is mounted read-only into the frontend container:
Rootless Container Support
The same Compose files work without modification under rootless runtimes.Docker Rootless Mode
Podman (Rootless by Default)
Podman runs rootless out of the box. No extra flags are needed:no-new-privileges, cap_drop) are supported in quadlet container files using the SecurityLabelDisable and AddCapability / DropCapability directives.
Security Checklist
Use this checklist before exposing Timeful to the internet:Generate a strong encryption key
The Store the output in your
ENCRYPTION_KEY is used to encrypt sensitive data in MongoDB. Generate it with:.env file or secret manager. If you lose this key, encrypted data becomes unrecoverable.Enable HTTPS via a reverse proxy
Never expose port 3002 directly to the internet. Terminate TLS at a reverse proxy (Nginx, Caddy, Traefik) and proxy to Nginx minimal TLS example:
localhost:3002.Caddy example (automatic HTTPS):Keep OAuth credentials out of version control
CLIENT_ID, CLIENT_SECRET, MICROSOFT_CLIENT_ID, and MICROSOFT_CLIENT_SECRET must never be committed to git. Keep them in .env (which is listed in .gitignore) or inject them as CI/CD secrets.Use Docker secrets for sensitive env vars in production
In high-security environments, replace plaintext Then reference the secret in
.env values with Docker secrets:docker-compose.yml instead of setting the variable directly.Back up MongoDB regularly
Run
make backup before every update and on a scheduled cron. See Updates & Backups for a recommended schedule.Keep Docker images up to date
Pull and restart on a regular cadence to pick up security patches in the base images (Go Alpine, nginx Alpine, MongoDB):
Verification Commands
Run these checks to confirm the security configuration is active in a running stack:SecurityOpt output:
Network Segmentation
All three containers communicate over the internaltimeful-network bridge. The backend container has no published ports — it is only reachable by the frontend container (and any other service on the same Docker network). Traffic from the internet reaches the frontend (port 3002 on the host), and the frontend proxies /api requests to the backend over the internal network.
Related Pages
Docker Deployment
Initial setup, Compose files, and configuration reference.
Troubleshooting
Solutions to the most common runtime issues.