Self-Hosting Guide
Self-host EdgeBase using Docker containers or direct Node.js execution.
Why Self-Hosting is Still Fast
EdgeBase runs on workerd, an open-source JavaScript runtime built on the V8 engine. The exact same code that runs on the cloud edge also runs in Docker/Node.js.
| Traditional BaaS | EdgeBase (Self-Hosted) | |
|---|---|---|
| DB Access | Network round-trip (ms) | In-process SQLite (μs) |
| JS Performance | Node.js (V8) | workerd (V8) — same engine |
| Cold Start | Container boot (seconds) | Always running (0ms) |
| WebSocket | Per-connection memory | Hibernation API — $0 idle memory |
SQLite runs in the same thread as the application, so single-query latency is significantly lower than BaaS platforms using network databases. Self-hosting doesn't come with a performance penalty — it can even be faster due to zero network hops.
Deployment Methods
| Cloud Edge | Docker | Direct | |
|---|---|---|---|
| Command | npx edgebase deploy | npx edgebase docker run | npx edgebase dev |
| Requires | Cloudflare account | Docker | Node.js 20.19+ (24.x recommended) |
| Pros | Global edge, auto-scale, no server management | Single container, data sovereignty | Simplest, run on any VPS |
| Cons | Cloud account required | Docker required | Process management needed for production |
| Cost | ~$5/mo | VPS only (~$5/mo) | VPS only |
| Data Location | Edge data centers | Local server | Local server |
1. Running with Docker
Quick Start
# Build image
npx edgebase docker build
# Run container (background) — auto-generates .env.release with JWT secrets
npx edgebase docker run -d
# Or use docker directly
docker build -t edgebase .
docker run -d -p 8787:8787 -v edgebase-data:/data --env-file .env.release --name edgebase edgebase
On first run, npx edgebase docker run automatically creates .env.release with secure random JWT_USER_SECRET and JWT_ADMIN_SECRET values. See the Environment Variables section below for details.
Docker Compose
# Start
docker compose up -d
# View logs
docker compose logs -f
# Stop
docker compose down
Environment Variables
Workers Secrets are mapped to environment variables in Docker:
# docker-compose.yml
services:
edgebase:
environment:
- JWT_USER_SECRET=your-secure-jwt-secret
- SERVICE_KEY=your-service-key
- JWT_ADMIN_SECRET=your-admin-jwt-secret
Or use an .env.release file (recommended — same file used by npx edgebase deploy):
# .env.release
JWT_USER_SECRET=your-secure-jwt-secret
SERVICE_KEY=your-service-key
JWT_ADMIN_SECRET=your-admin-jwt-secret
docker run --env-file .env.release -p 8787:8787 -v edgebase-data:/data edgebase
That SERVICE_KEY is the same credential consumed by all Admin SDKs.
For local Docker development, use .env.development instead:
docker run --env-file .env.development -p 8787:8787 -v edgebase-data:/data edgebase
Data Persistence
All data is stored in the /data volume:
| Data | Path | Description |
|---|---|---|
| DO SQLite | /data/v3/do/ | Database DO state (isolated namespaces, Room/DatabaseLive support data) |
D1 Auth (AUTH_DB) | /data/v3/d1/ | Auth control plane (users, sessions, OAuth, MFA, admin data) |
D1 Control (CONTROL_DB) | /data/v3/d1/ | Internal operational metadata (plugin versions, cleanup/backup metadata) |
| R2 Files | /data/v3/r2/ | Uploaded files |
| KV Data | /data/v3/kv/ | OAuth state, membership cache |
AUTH_DB and CONTROL_DB are separate internal D1 databases that share the same persisted base directory. Keeping plugin/control-plane metadata in CONTROL_DB avoids mixing operational state into the auth hot path.
2. Direct workerd Execution
Run directly through the EdgeBase CLI (which starts wrangler dev) on a machine with Node.js:
# Clone or initialize a EdgeBase project
npx edgebase init my-project
cd my-project
# Start the local workerd runtime
npx edgebase dev --port 8787
# Advanced/manual: only when you provide a complete Wrangler config yourself
npx wrangler dev --config ./wrangler.toml --port 8787 --persist-to ./data
npx edgebase dev is the recommended path. It evaluates edgebase.config.ts and injects the managed bindings needed for local development before starting Wrangler.
Use raw wrangler dev only for explicit manual setups, such as a dedicated test config, or when your wrangler.toml already includes every binding your project needs.
Process Management (PM2 Recommended)
# Install PM2
npm install -g pm2
# Start EdgeBase
pm2 start "npx edgebase dev --port 8787" --name edgebase
# Configure auto-restart
pm2 startup
pm2 save
3. HTTPS Reverse Proxy
HTTPS is required for production. Use Caddy or Nginx as a reverse proxy.
EdgeBase uses the client IP address for rate limiting and brute-force protection. On Cloudflare Edge, it trusts CF-Connecting-IP. In self-hosted environments (Docker / Direct), it only trusts X-Forwarded-For when you set trustSelfHostedProxy: true in edgebase.config.ts.
If EdgeBase is exposed directly to the internet without a reverse proxy, leave trustSelfHostedProxy: false so spoofed X-Forwarded-For headers are ignored. If you do run behind Nginx or Caddy, enable trustSelfHostedProxy: true and make sure the proxy overwrites X-Forwarded-For.
Without trustSelfHostedProxy: true, self-hosted deployments will treat requests as coming from the proxy itself for IP-based features. That is safer by default, but less precise operationally.
import { defineConfig } from '@edgebase/shared';
export default defineConfig({
trustSelfHostedProxy: true,
});
Caddy (Recommended — Auto HTTPS)
# Install Caddy
sudo apt install -y caddy
Caddyfile configuration:
your-domain.com {
reverse_proxy localhost:8787 {
# EdgeBase reads X-Forwarded-For only when trustSelfHostedProxy: true.
# Overwrite the header with the real client IP.
header_up X-Forwarded-For {remote_host}
}
}
sudo systemctl reload caddy
Caddy automatically configures Let's Encrypt. No manual SSL certificate management needed. When
trustSelfHostedProxy: trueis enabled, EdgeBase uses the overwrittenX-Forwarded-Forvalue for IP-based features.
Nginx + Let's Encrypt
# Install Nginx + Certbot
sudo apt install -y nginx certbot python3-certbot-nginx
Nginx configuration (/etc/nginx/sites-available/edgebase):
server {
server_name your-domain.com;
location / {
proxy_pass http://localhost:8787;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# IMPORTANT: Use $remote_addr (not $proxy_add_x_forwarded_for) to prevent
# clients from injecting fake IPs. This overwrites any client-sent header.
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Enable site + SSL
sudo ln -s /etc/nginx/sites-available/edgebase /etc/nginx/sites-enabled/
sudo certbot --nginx -d your-domain.com
sudo systemctl reload nginx
The Upgrade header configuration is required for WebSocket support.
If you enable Service Key ipCidr constraints or rely on per-client rate limiting while self-hosting, trustSelfHostedProxy: true plus a correctly configured reverse proxy is required.
4. Backups
EdgeBase provides two backup methods:
| Method | Use Case | Speed |
|---|---|---|
| Volume Copy | Restore within the same environment (Docker→Docker, Direct→Direct) | Fast |
| CLI Portable Backup | Cross-environment migration (Edge↔Docker↔Direct) | Moderate |
4.1 Volume Backup (Same Environment)
The fastest method — directly copy Docker volumes or the .wrangler/state/ directory.
Docker Volume Backup
# Backup volume (tar archive)
docker run --rm -v edgebase-data:/data -v $(pwd):/backup \
alpine tar czf /backup/edgebase-backup-$(date +%Y%m%d).tar.gz /data
# Restore volume
docker run --rm -v edgebase-data:/data -v $(pwd):/backup \
alpine tar xzf /backup/edgebase-backup-20260213.tar.gz -C /
Direct Execution Backup
# Backup the .wrangler/state/ directory
tar czf edgebase-backup-$(date +%Y%m%d).tar.gz .wrangler/state/
# Restore
tar xzf edgebase-backup-20260213.tar.gz
Volume copies only work within the same environment (Docker→Docker, Direct→Direct). For cross-environment migration (e.g., Docker→Edge), use the CLI portable backup below.
4.2 CLI Portable Backup (Cross-Environment)
# DB only (default, fast)
npx edgebase backup create --url <URL> --service-key <KEY>
# DB + secrets (for environment migration — preserves existing JWTs)
npx edgebase backup create --include-secrets
# DB + R2 files (large, slow)
npx edgebase backup create --include-storage
# Full backup (complete migration)
npx edgebase backup create --include-secrets --include-storage
# Backup from Edge environment (enumerates DOs via CF API)
npx edgebase backup create --account-id <CF_ACCOUNT_ID> --api-token <CF_API_TOKEN>
Restore:
# Restore (Wipe & Restore — replaces all existing data)
npx edgebase backup restore --from backup.json --url <target-URL> --service-key <KEY>
# Restore to Edge target
npx edgebase backup restore --from backup.json --account-id <ID> --api-token <TOKEN>
When using --include-secrets, the backup file contains sensitive information. File permissions are automatically set to 600.
4.3 Automated Backup (Cron)
# Daily backup at 3 AM (Docker Volume)
echo "0 3 * * * docker run --rm -v edgebase-data:/data -v /backups:/backup alpine tar czf /backup/edgebase-\$(date +\\%Y\\%m\\%d).tar.gz /data" | crontab -
5. Monitoring
Health Check
curl http://localhost:8787/api/health
# → {"status":"ok","version":"0.1.0","timestamp":"2026-03-17T12:00:00.000Z"}
Docker Logs
# Real-time logs
docker logs -f edgebase
# Last 100 lines
docker logs --tail 100 edgebase
Admin Dashboard
The Admin Dashboard is built into self-hosted deployments:
http://your-domain.com/admin
An admin account setup screen is displayed on first access.
If you lose an admin password later, recover it with npx edgebase admin reset-password using a configured root Service Key. Admin recovery is CLI-based rather than email-based.
6. Troubleshooting
| Problem | Solution |
|---|---|
| Port conflict | Use --port to specify a different port |
| Data loss | Verify volume mount: -v edgebase-data:/data |
| WebSocket disconnects | Check reverse proxy Upgrade header configuration |
| Container not auto-restarting | Verify --restart unless-stopped flag |
| Out of memory | Set memory limit with --memory 512m |