Self-hosted stack compose files and deployment notes
| dashboard | ||
| notifier | ||
| apprise.compose.yml | ||
| changedetection.compose.yml | ||
| forgejo.compose.yml | ||
| miniflux.compose.yml | ||
| n8n.compose.yml | ||
| paperless.compose.yml | ||
| README.md | ||
| seed_changedetection.py | ||
| seed_miniflux.py | ||
| write_homepage_config.py | ||
Manohar's Infrastructure
Self-hosted stack on Hetzner CX32 (Helsinki), deployed via Dokploy + Traefik.
Services
App URL Purpose Forgejo https://git.manohargupta.com Self-hosted Git
n8n https://automate.manohargupta.com Workflow automation
Apprise https://notify.manohargupta.com Notification API (Tailscale only)
Miniflux https://feeds.manohargupta.com RSS reader
ChangeDetection https://watch.manohargupta.com Webpage change monitor
Paperless-ngx https://docs.manohargupta.com Document OCR + search
Tiger Agent https://agent.manohargupta.com AI orchestration
Dokploy https://dokploy.manohargupta.com Docker orchestration
Uptime Kuma https://status.manohargupta.com Monitoring
Umami https://analytics.manohargupta.comWeb analytics
Stack
- Server: Hetzner CX32, Ubuntu 24.04, Helsinki (hel1)
- Orchestration: Dokploy (Docker Swarm)
- Reverse proxy: Traefik with Let's Encrypt TLS
- Access: Tailscale SSH only (
ssh root@manohar-ubuntu) - DNS: Namecheap → manohargupta.com
Deployment notes
- All compose files are in
deployments/ - Every compose declares
dokploy-networkasexternal: true - Labels duplicated under both
labels:anddeploy.labels:— required because Dokploy uses swarm stack deploy for multi-service composes (swarm provider reads deploy.labels; docker provider reads labels) - Secrets set in Dokploy Env tab, never hardcoded (except bcrypt hashes in labels which Dokploy cannot substitute)
- Apprise and ChangeDetection restricted to Tailscale CGNAT range (100.64.0.0/10)
Key learnings
- Forgejo Docker image uses /data as root, not /var/lib/gitea
- n8n WEBHOOK_URL must match public domain exactly or webhook URLs are wrong
- htpasswd hashes in Traefik labels need
escaping (Compose interpolates single $) - Dokploy env var substitution works in environment: blocks but NOT in labels: