Self-hosted stack compose files and deployment notes
Find a file
Mannu 88f8876829 fix(obsidian-sync): run CouchDB as uid 5984 to bypass entrypoint chown
The CouchDB 3.3 entrypoint runs as root and executes:
  find /opt/couchdb \! -user couchdb -exec chown couchdb {} +
before writing a single log line. The :ro bind-mounted local.ini is
owned by root on the host, so chown fails with EROFS — set -e exits
immediately with code 1 and zero output (381ms crash, empty logs).

Running as user 5984:5984 skips the root block in the entrypoint
entirely, going straight to admin setup and CouchDB launch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 09:38:15 +05:30
dashboard dashboard: replace Homepage with static nginx — clean React app 2026-05-02 14:00:12 +05:30
notifier notifier: refactor watcher.py -- secrets to .env, send via Apprise, systemd service 2026-04-27 01:28:03 +05:30
obsidian-sync fix(obsidian-sync): run CouchDB as uid 5984 to bypass entrypoint chown 2026-06-07 09:38:15 +05:30
apprise.compose.yml apprise: switch from IP whitelist to basic auth 2026-04-27 00:04:33 +05:30
changedetection.compose.yml Initial commit: all 6 self-hosted app compose files + README 2026-04-26 23:08:58 +05:30
forgejo.compose.yml Initial commit: all 6 self-hosted app compose files + README 2026-04-26 23:08:58 +05:30
miniflux.compose.yml Initial commit: all 6 self-hosted app compose files + README 2026-04-26 23:08:58 +05:30
n8n.compose.yml Initial commit: all 6 self-hosted app compose files + README 2026-04-26 23:08:58 +05:30
paperless.compose.yml Initial commit: all 6 self-hosted app compose files + README 2026-04-26 23:08:58 +05:30
README.md Update README.md 2026-05-29 13:25:19 +00:00
seed_changedetection.py homepage: config writer + seed scripts for miniflux and changedetection 2026-04-30 22:41:10 +05:30
seed_miniflux.py homepage: config writer + seed scripts for miniflux and changedetection 2026-04-30 22:41:10 +05:30
write_homepage_config.py homepage: config writer + seed scripts for miniflux and changedetection 2026-04-30 22:41:10 +05:30

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.com Web 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-network as external: true
  • Labels duplicated under both labels: and deploy.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: