Deployment
This guide covers production deployment on Unraid using Docker Compose, with a Cloudflare tunnel for HTTPS and GitHub Actions for automatic deploys.
Architecture
git push → GitHub Actions → SSH (Tailscale) → Unraid server
↓
docker compose up --build
↓
[geekonomics container :3001]
↓
[cloudflared tunnel container]
↓
your-domain.com (HTTPS)Cloudflare tunnel setup
A Cloudflare tunnel exposes your container to the internet without opening ports on your router.
1. Create the tunnel
cloudflared tunnel login
cloudflared tunnel create geekonomicsThis generates a credentials file at ~/.cloudflared/<tunnel-id>.json. Copy it into the repo:
cp ~/.cloudflared/<tunnel-id>.json /path/to/geekonomics-app/cloudflared/2. Edit cloudflared/config.yml
tunnel: <your-tunnel-id>
credentials-file: /etc/cloudflared/<your-tunnel-id>.json
ingress:
- hostname: books.yourdomain.com
service: http://bookkeeping:3001
- service: http_status:4043. Add a CNAME in Cloudflare DNS
| Field | Value |
|---|---|
| Type | CNAME |
| Name | books (or your subdomain) |
| Target | <tunnel-id>.cfargotunnel.com |
| Proxy | Enabled (orange cloud) |
GitHub Actions automatic deploys
Every push to main SSHes into your Unraid server and runs git pull && docker compose up -d --build.
Deploy key setup
The Unraid server needs its own SSH key registered as a GitHub deploy key (one key per repo — GitHub rejects reuse).
On your Unraid server:
ssh-keygen -t ed25519 -C "geekonomics-deploy" -f ~/.ssh/geekonomics_deploy -N ""
cat ~/.ssh/geekonomics_deploy.pub # copy thisAdd the public key in GitHub: Settings → Deploy keys → Add deploy key (read-only).
Add an SSH config alias so git picks the right key:
cat >> ~/.ssh/config << 'EOF'
Host github-geekonomics
HostName github.com
User git
IdentityFile ~/.ssh/geekonomics_deploy
EOFSet the remote to use the alias:
cd /path/to/geekonomics-app
git remote set-url origin git@github-geekonomics:oneofthegeeks/geekonomics-app.gitGitHub Actions secrets
Go to Settings → Secrets and variables → Actions and add:
| Secret | Value |
|---|---|
DEPLOY_HOST | Your server's Tailscale IP (e.g. 100.x.x.x) — not the LAN IP |
DEPLOY_USER | root |
DEPLOY_SSH_KEY | Your dev machine's private key (the one in the server's authorized_keys) |
DEPLOY_PORT | 22 |
DEPLOY_PATH | /mnt/user/appdata/geekonomics-app |
TS_OAUTH_CLIENT_ID | Tailscale OAuth client ID |
TS_OAUTH_CLIENT_SECRET | Tailscale OAuth client secret |
Use the Tailscale IP
DEPLOY_HOST must be the Tailscale IP (100.x.x.x), not the LAN IP. The GitHub Actions runner joins Tailscale and routes via that network — it cannot reach your LAN IP directly.
Get your Tailscale IP at login.tailscale.com or by running tailscale ip on the server.
Tailscale OAuth credentials
Go to login.tailscale.com/admin/settings/oauth and create a new OAuth client with devices:write scope. Use the client ID and secret for TS_OAUTH_CLIENT_ID and TS_OAUTH_CLIENT_SECRET.
Manual deploy
If you need to deploy without waiting for GitHub Actions:
ssh root@your-server-tailscale-ip
cd /mnt/user/appdata/geekonomics-app
git pull origin main
docker compose up -d --build
docker image prune -fDeploy time is typically 2–3 minutes — the Vite frontend build is the slow part.
Data persistence
Your data lives in bind mounts outside the container and survives rebuilds:
geekonomics-app/
├── data/geekonomics.db — SQLite database
├── data/sessions.db — Session store
└── uploads/ — Receipt attachmentsNever delete these directories. Rebuilding the container image is safe; deleting the bind-mount directories is not.