Deploy your own simpleSuite backend and frontends with Docker Compose. Open-source, self-hostable, and privacy-first.
This repo contains everything needed to self-host the simpleSuite ecosystem — a free, open-source alternative to hosted productivity and budgeting apps. Run your own Supabase-compatible backend (and optionally the frontends) on your own hardware with a single docker compose up.
Two deployment modes:
- Backend-only — Self-host the database and API. Use the hosted frontends at budget.simplesuite.dev or tracker.simplesuite.dev.
- Full stack — Self-host everything, including the frontend served via Caddy.
- Supabase Cloud (managed)
- Firebase
- Appwrite
- PocketBase
- Hosted YNAB / Todoist / Notion backends
| App | Frontend Repo | Hosted URL |
|---|---|---|
| simpleBudget | simplesuite/simplebudget | budget.simplesuite.dev |
| simpleTracker | simplesuite/simpletracker | tracker.simplesuite.dev |
Both apps share the same Supabase backend. A single self-hosted instance supports both.
# 1. Clone this repo
git clone https://github.com/simplesuite/simplesuite-selfhost.git
cd simplesuite-selfhost
# 2. Create and populate your environment file
cp deploy/.env.example deploy/.env
cd deploy && bash scripts/generate-secrets.sh && cd ..
# 3. Start the backend
docker compose -f deploy/compose.yml up -d
# 4. (Optional) Start with a frontend included
# Set FRONTEND_APP in deploy/.env to: simplebudget or simpletracker
docker compose -f deploy/compose.yml -f deploy/compose.frontend.yml up -dThe API gateway will be available at http://localhost:8000.
The frontend (if enabled) will be at http://localhost:8080.
| Requirement | Notes |
|---|---|
| Docker + Compose v2 | docker compose version should return 2.x+ |
| Bash shell | Git Bash on Windows; native on macOS/Linux |
openssl, base64 |
Required by the secret generator script |
Windows users: Run all
bashcommands from Git Bash, not PowerShell or CMD.
Run your own Supabase-compatible backend while using the hosted frontends.
cp deploy/.env.example deploy/.env
cd deploy && bash scripts/generate-secrets.sh && cd ..
docker compose -f deploy/compose.yml up -dVerify everything is healthy:
docker compose -f deploy/compose.yml psThen open the hosted frontend and use the Config Backend page to enter:
- URL:
http://localhost:8000(or your server's address) - Anon Key: The
ANON_KEYvalue fromdeploy/.env
This works for both simpleBudget and simpleTracker — they share the same backend.
Run the entire stack — backend and frontend — on your own machine.
cp deploy/.env.example deploy/.env
cd deploy && bash scripts/generate-secrets.sh && cd ..
# Edit deploy/.env to set which app to deploy:
# FRONTEND_APP=simplebudget (default)
# FRONTEND_APP=simpletracker
docker compose -f deploy/compose.yml -f deploy/compose.frontend.yml up -dOpen http://localhost:8080 in your browser.
The frontend container clones the latest source from the selected app's repo, builds it, and serves it via Caddy. The config.js is auto-generated from your .env values at container startup — no manual configuration needed.
To self-host both apps simultaneously, use the all-frontends compose file:
docker compose -f deploy/compose.yml -f deploy/compose.allfrontends.yml up -dThis starts:
- simpleBudget at http://localhost:8080 (configurable via
SIMPLEBUDGET_PORT) - simpleTracker at http://localhost:8081 (configurable via
SIMPLETRACKER_PORT)
All config lives in deploy/.env. Run generate-secrets.sh to auto-fill secrets.
| Variable | Default | Scope | Description |
|---|---|---|---|
SUPABASE_PUBLIC_URL |
http://localhost:8000 |
Public | Kong API gateway URL (browser-accessible) |
ANON_KEY |
(generated) | Public | Browser-safe JWT for unauthenticated API access |
SERVICE_ROLE_KEY |
(generated) | Private | Elevated JWT — bypasses RLS |
JWT_SECRET |
(generated) | Private | Signs all JWTs |
POSTGRES_PASSWORD |
(generated) | Private | Database password |
DASHBOARD_USERNAME |
admin |
Optional | Studio dashboard username |
DASHBOARD_PASSWORD |
(generated) | Optional | Studio dashboard password |
VAPID_PUBLIC_KEY |
(empty) | Push | Web Push public key (injected into frontend) |
VAPID_PRIVATE_KEY |
(empty) | Private/Push | Web Push private key (used by notify worker) |
VAPID_SUBJECT |
mailto:admin@example.com |
Push | Contact email for push service operators |
CHECK_INTERVAL_MS |
900000 |
Push | How often the worker checks for due items (ms) |
FRONTEND_APP |
simplebudget |
Frontend | Which app to build: simplebudget or simpletracker |
FRONTEND_PORT |
8080 |
Frontend | Port for single-app mode (compose.frontend.yml) |
SIMPLEBUDGET_PORT |
8080 |
Frontend | simpleBudget port for both-apps mode (compose.allfrontends.yml) |
SIMPLETRACKER_PORT |
8081 |
Frontend | simpleTracker port for both-apps mode (compose.allfrontends.yml) |
| Safe for browsers | Never expose |
|---|---|
SUPABASE_PUBLIC_URL |
SERVICE_ROLE_KEY |
ANON_KEY |
JWT_SECRET |
VAPID_PUBLIC_KEY |
VAPID_PRIVATE_KEY |
POSTGRES_PASSWORD |
deploy/.envis in.gitignore— never commit it.- The frontend container only receives
SUPABASE_PUBLIC_URLandANON_KEY.
Connect with pgAdmin or any Postgres client:
| Setting | Value |
|---|---|
| Host | localhost |
| Port | 5432 |
| Database | postgres |
| Username | supabase_admin |
| Password | Your POSTGRES_PASSWORD |
git pull origin main
# Backend-only
docker compose -f deploy/compose.yml down
docker compose -f deploy/compose.yml up -d
# Full stack (rebuilds frontend with latest source)
docker compose -f deploy/compose.yml -f deploy/compose.frontend.yml down
docker compose -f deploy/compose.yml -f deploy/compose.frontend.yml up -d --buildMigrations run automatically on every startup.
The simplest way to access your instance from other devices — no port forwarding, no reverse proxy, automatic HTTPS.
tailscale serve 8000 # Expose API gateway
tailscale serve --https 8080 # Expose frontendUpdate SUPABASE_PUBLIC_URL in deploy/.env to your Tailscale URL and restart.
Send push reminders to users' devices — even when the app isn't open. The notification worker checks for due tasks (simpleTracker) and upcoming transactions (simpleBudget) on a schedule and delivers Web Push notifications to subscribed devices.
- Users toggle "Notifications" in the app settings → browser subscribes to push and stores credentials in the
push_subscriptionstable. - The
notify-workercontainer runs every 15 minutes (configurable), queries PostgREST for due/overdue items, and sends Web Push to all active subscriptions. - The Service Worker in each app receives the push and shows a native notification — works on desktop and mobile, even when the app tab is closed.
VAPID keys are generated automatically by the generate-secrets.sh script (the same one that creates your JWT and database secrets). If you've already run it before adding push support, just run it again — it only fills in missing values:
cd deploy && bash scripts/generate-secrets.sh && cd ..Verify the keys were added to your deploy/.env:
grep VAPID deploy/.envThen restart the stack:
docker compose -f deploy/compose.yml down
docker compose -f deploy/compose.yml up -d --buildThe notify-worker service starts automatically alongside the rest of the backend. The VAPID public key is injected into the frontend's config.js at container startup.
| Variable | Default | Description |
|---|---|---|
VAPID_PUBLIC_KEY |
(empty — push disabled) | Public key shared with browsers |
VAPID_PRIVATE_KEY |
(empty) | Private key for signing push messages |
VAPID_SUBJECT |
mailto:admin@example.com |
Contact URI for push service operators |
CHECK_INTERVAL_MS |
900000 (15 min) |
How often the worker checks for due items |
Note: If
VAPID_PUBLIC_KEYorVAPID_PRIVATE_KEYare not set, the notify worker will exit on startup with an error message. Push notifications are fully opt-in — the rest of the stack works fine without them.
| App | Trigger | Notification |
|---|---|---|
| simpleTracker | Tasks due today or overdue | "2 Task Reminders — 1 overdue, 1 due today" |
| simpleBudget | Transactions scheduled for tomorrow | "Transaction Reminder — Tomorrow: Rent ($1500.00)" |
The same worker can run against Supabase Cloud — just point the env vars at your project:
POSTGREST_URL=https://yourproject.supabase.co/rest/v1
SERVICE_ROLE_KEY=your-service-role-key
VAPID_PUBLIC_KEY=...
VAPID_PRIVATE_KEY=...You can run the worker as a standalone container, a cron job, or on any serverless platform that supports scheduled execution.
Simply remove (or leave empty) the VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY from your .env. The worker won't start, and the frontends won't show the push subscription option to users.
Postgres won't start
docker compose -f deploy/compose.yml logs postgresEnsure POSTGRES_PASSWORD is set. To reset completely:
docker compose -f deploy/compose.yml down -v
docker compose -f deploy/compose.yml up -dMigration errors
docker compose -f deploy/compose.yml logs migrationsTo re-run from scratch (destroys data):
docker compose -f deploy/compose.yml down -v
docker compose -f deploy/compose.yml up -dGoTrue (auth) fails
docker compose -f deploy/compose.yml logs gotrueUsually caused by role passwords not being set. Fix with a full reset:
docker compose -f deploy/compose.yml down -v
docker compose -f deploy/compose.yml up -dCORS errors in browser
Handled by the Kong CORS plugin in deploy/kong.yml. If a header is blocked, add it to the headers list and restart:
docker compose -f deploy/compose.yml restart kongComplete reset
docker compose -f deploy/compose.yml down -v
rm deploy/.env
cp deploy/.env.example deploy/.env
cd deploy && bash scripts/generate-secrets.sh && cd ..
docker compose -f deploy/compose.yml up -dNotify worker not sending notifications
docker compose -f deploy/compose.yml logs notify-workerCommon causes:
- VAPID keys not set — the worker exits immediately with "Missing required env var" message. Generate keys with
npx web-push generate-vapid-keys. - No subscriptions in database — users need to enable notifications in the app settings first.
- PostgREST not reachable — ensure the worker can reach
http://postgrest:3000(default internal URL). - Expired subscriptions — the worker automatically removes 410/404 subscriptions. If all subscriptions are expired, no notifications will be sent.
Check subscription count:
docker compose -f deploy/compose.yml exec postgres psql -U postgres -c "SELECT app, count(*) FROM push_subscriptions GROUP BY app;" ┌─────────────────────────────────────────┐
│ Docker Compose │
│ │
Browser ──────────►│ Kong :8000 (API Gateway) │
│ ├── /rest/v1/* → PostgREST :3000 │
│ └── /auth/v1/* → GoTrue :9999 │
│ │
│ Postgres :5432 │
│ Migrations (runs once at startup) │
│ │
│ notify-worker (every 15min) │
│ └── PostgREST → Web Push delivery │
│ │
│ Frontend :8080 (optional, Caddy) │
└─────────────────────────────────────────┘
Contributions are welcome! Please open an issue or submit a pull request.
This project is licensed under the GNU AGPL v3.
