Troubleshooting
Common issues when setting up and running sfp server.
General
denied: denied from Docker login
Stale token, wrong registry
docker logout <registry> && echo "<pat>" | docker login <registry> -u <user> --password-stdin
Domain returns NXDOMAIN
DNS not propagated or A record missing
nslookup <domain>; verify A record points at server; allow up to 48h propagation
Need raw container logs
Compose project name matches tenant
cd /opt/sfp-server/tenants/<tenant> && docker compose -p <tenant> logs -f
TLS / Certificates
Caddy logs cert file not found
TLS cert not placed or not decoded
Re-run init with ORIGIN_CERT/ORIGIN_KEY secrets, or manually place origin.pem + origin-key.pem in {tenantDir}/certs/
Let's Encrypt fails to issue cert
DNS not publicly resolvable or port 80 blocked
Verify dig <domain> returns server IP from the public internet; open port 80 inbound for ACME challenge
Certificate format error
Cert not PEM-encoded or not base64
Certs must be PEM format; .crt/.key files are typically already PEM — rename and base64-encode: base64 -w 0 origin.pem
Services
external volume "…" not found
Volumes not created yet
Use sfp server start (not raw docker compose up); it creates external volumes automatically
Server healthy but curl returns 502
App server not ready yet
Wait 30–60s after start; check sfp server logs --service app; Caddy shows a maintenance page until backend is up
Supabase containers not starting
Missing compose profile
sfp server start activates the supabase profile automatically; raw docker compose up does not
VaultBootstrapService fetch failed
Supabase auth not ready at server boot
Restart the server container: docker compose -p <tenant> restart server; auth containers need ~15s to initialize
SSH
sfp server init: SSH connection error: Timed out while waiting for handshake then Docker is not installed or not available
Target port 22 unreachable (firewall, security group, bastion-only network). The "Docker" line is misleading — read the SSH line above it.
Open a local tunnel via the bastion: ssh -fNT -L 2222:<target-host>:22 <user>@<bastion>, then sfp server init --ssh-connection <user>@127.0.0.1:2222 --identity-file ~/.ssh/<key>; tear down with pkill -f "ssh -fNT -L 2222".
sfp server init: getaddrinfo EAI_AGAIN <host-alias>
--ssh-connection was passed an ~/.ssh/config Host alias. sfp uses Node ssh2, which does not read ~/.ssh/config — Host, ProxyJump, IdentityFile, User are all ignored.
Pass a literal user@host[:port] plus --identity-file <path>. For ProxyJump-style routing, use the local tunnel recipe in the row above.
sfp server init: Failed to read private key: ENOENT <path>
--identity-file path missing or unreadable by the user running sfp.
Pass an absolute path: --identity-file /home/<user>/.ssh/<key>. ~/ expands; $HOME does not.
sfp server init: No authentication method provided
Neither --identity-file nor --passphrase passed. sfp does not fall back to ssh-agent or ~/.ssh/id_rsa.
Always pass --identity-file <path> with --ssh-connection. No --use-agent flag exists.
Authentication
GitHub OAuth callback URL mismatch
Callback URL doesn't match domain
Must be exactly https://<your-domain>/auth/v1/callback — check GitHub OAuth App settings
Cloud Supabase
init cannot reach Supabase Cloud
Project IP allowlist or rotated key
Verify allowlist covers server IP; curl -X GET "<url>/rest/v1/" -H "apikey: <anon>" to probe; check service key
network is unreachable for IPv6 ([2a05:d014:…])
Server has no IPv6 (common on Hetzner)
Switch SUPABASE_DB_URL to Session pooler (port 6543) from Supabase Dashboard → Connect
Same as above (alternatives)
Same
Buy Supabase IPv4 add-on; or enable host IPv6
Security best practices
Firewall (UFW)
sudo ufw allow 22,80,443/tcp && sudo ufw enable — SSH + HTTP redirect + HTTPS only
Host updates
Enable unattended security updates
Secret rotation
Rotate DOCKER_REGISTRY_TOKEN + Supabase service keys quarterly
Log shipping
Ship server logs to a central store (don't grep on the box)
Tenant backups
Snapshot <base-dir>/tenants/<tenant>/ — covers .env, generated compose, admin credentials.json
Database backups
Snapshot Supabase (cloud or self-hosted Postgres) on its own schedule — independent of tenant files
Admin dashboards
Restrict ports 8080, 3100, 4873 to admin IPs via ALLOWED_IPS in .env; restart Caddy after changes
Last updated
Was this helpful?