Deploy SporeDB on DigitalOcean¶
Deploy SporeDB on DigitalOcean App Platform with managed PostgreSQL and Spaces (S3-compatible storage). Estimated time: 10 minutes.
Prerequisites¶
| Requirement | Details |
|---|---|
| DigitalOcean account | With billing enabled |
doctl CLI |
Installed and authenticated (doctl auth init) |
| GitHub repo | SporeDB repo forked to your account or connected as a source in App Platform |
Quick Start¶
Go to cloud.digitalocean.com > App Platform > Create App, select your SporeDB GitHub repo, and follow the wizard. Or use the CLI:
Note: SporeDB does not ship a
.do/app-spec.yamlfile. This guide focuses on the console walkthrough. See the step-by-step instructions below.
Step-by-Step Setup¶
1. Connect Your GitHub Repository¶
- Go to cloud.digitalocean.com > App Platform > Create App
- Select GitHub as the source
- Authorize DigitalOcean to access your GitHub account (if not already done)
- Select the SporeDB repository and the
mainbranch
2. Configure the App Component¶
- Set the component name to
sporedb - Set the type to Web Service
- Set the Dockerfile path to
Dockerfile - Set the HTTP port to
8000
3. Add a Managed PostgreSQL Database¶
- Click Add Resource > Database
- Select PostgreSQL
- Choose the Development plan ($7/mo, single node)
- Name the database
sporedb-db
4. Create a Spaces Bucket (S3-Compatible Storage)¶
Spaces is created separately from App Platform:
- Go to cloud.digitalocean.com > Spaces Object Storage > Create a Space
- Choose a region (e.g.,
nyc3) - Name the Space
sporedb - Note the endpoint URL (e.g.,
https://nyc3.digitaloceanspaces.com) - Go to API > Spaces Keys > Generate New Key
- Save the access key and secret key
5. Generate Ed25519 Signing Keys¶
SporeDB uses Ed25519 key pairs for JWT authentication and audit trail signing. Generate them locally:
openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
Base64-encode for upload as environment variables:
# Cross-platform single-line base64 (works on both macOS and Linux):
openssl base64 -A -in private.pem # Set as SPOREDB_ED25519_PRIVATE_KEY_B64
openssl base64 -A -in public.pem # Set as SPOREDB_ED25519_PUBLIC_KEY_B64
6. Set Environment Variables¶
In the App Platform settings, add the following environment variables:
| Variable | Value | Secret? |
|---|---|---|
SPOREDB_MODE |
selfhosted |
No |
SPOREDB_SERVER_PORT |
8000 |
No |
SPOREDB_DATABASE_URL |
postgresql+asyncpg://user:pass@host:25060/db?sslmode=require |
Yes |
SPOREDB_S3_ENDPOINT |
https://nyc3.digitaloceanspaces.com |
No |
SPOREDB_S3_ACCESS_KEY |
(your Spaces API key) | Yes |
SPOREDB_S3_SECRET_KEY |
(your Spaces API secret) | Yes |
SPOREDB_S3_BUCKET |
sporedb |
No |
SPOREDB_ED25519_PRIVATE_KEY_B64 |
(base64-encoded private key) | Yes |
SPOREDB_ED25519_PUBLIC_KEY_B64 |
(base64-encoded public key) | Yes |
WARNING: Do NOT use the auto-injected
${db.DATABASE_URL}variable directly. DigitalOcean injects it inpostgresql://format, but SporeDB requirespostgresql+asyncpg://format. You MUST setSPOREDB_DATABASE_URLmanually with the correct prefix.To construct the URL: go to the database component in App Platform, copy the connection details (host, port, username, password, database name), and assemble them as:
postgresql+asyncpg://username:password@host:25060/dbname?sslmode=require
7. Override the Docker Command¶
SporeDB loads Ed25519 keys from file paths only. On App Platform, container filesystems are ephemeral. Override the Docker command to decode base64-encoded keys at startup.
In the app component settings, set the Run Command to:
sh -c 'mkdir -p /home/sporedb/app/keys && echo "$SPOREDB_ED25519_PRIVATE_KEY_B64" | base64 -d > /home/sporedb/app/keys/cloud_private.pem && echo "$SPOREDB_ED25519_PUBLIC_KEY_B64" | base64 -d > /home/sporedb/app/keys/cloud_public.pem && export SPOREDB_JWT_SECRET_KEY_PATH=/home/sporedb/app/keys/cloud_private.pem && export SPOREDB_JWT_PUBLIC_KEY_PATH=/home/sporedb/app/keys/cloud_public.pem && exec uvicorn sporedb.cloud.app:create_app --factory --host 0.0.0.0 --port 8000'
This command:
- Creates the keys directory
- Decodes the base64-encoded Ed25519 keys to PEM files
- Sets the key file path environment variables
- Launches the SporeDB server
8. Deploy¶
Click Create Resources and wait for the build and deployment to complete. App Platform will build the Docker image from the repository and start the service.
9. Verify¶
Once deployed, check the app URL:
The health endpoint should return HTTP 200.
What Gets Provisioned¶
| Resource | Specification |
|---|---|
| App container | Basic instance, 512 MB RAM |
| Managed PostgreSQL | Basic (256 MB), single node |
| Spaces bucket | User-created separately |
Configuration¶
| Variable | Source | Description |
|---|---|---|
SPOREDB_MODE |
App env var | selfhosted |
SPOREDB_SERVER_PORT |
App env var | 8000 |
SPOREDB_S3_ENDPOINT |
App env var | Spaces endpoint URL |
SPOREDB_S3_BUCKET |
App env var | sporedb |
SPOREDB_DATABASE_URL |
App env var (encrypted) | postgresql+asyncpg://... (manual, not auto-injected) |
SPOREDB_S3_ACCESS_KEY |
App env var (encrypted) | Spaces API key |
SPOREDB_S3_SECRET_KEY |
App env var (encrypted) | Spaces API secret |
SPOREDB_ED25519_PRIVATE_KEY_B64 |
App env var (encrypted) | Base64-encoded Ed25519 private key |
SPOREDB_ED25519_PUBLIC_KEY_B64 |
App env var (encrypted) | Base64-encoded Ed25519 public key |
Connection String Format¶
WARNING: DigitalOcean's auto-injected
${db.DATABASE_URL}usespostgresql://format. SporeDB requirespostgresql+asyncpg://format. You MUST setSPOREDB_DATABASE_URLmanually with the correct prefix.Correct:
postgresql+asyncpg://user:pass@host:25060/db?sslmode=requireWrong:postgresql://user:pass@host:25060/db?sslmode=require
Ed25519 Key Setup¶
SporeDB loads Ed25519 signing keys from file paths only. On DigitalOcean App Platform, container filesystems are ephemeral -- key files do not persist across deploys. The Docker command override handles this by:
- Reading
SPOREDB_ED25519_PRIVATE_KEY_B64andSPOREDB_ED25519_PUBLIC_KEY_B64from encrypted environment variables - Decoding the base64 values to PEM files at
/home/sporedb/app/keys/ - Setting
SPOREDB_JWT_SECRET_KEY_PATHandSPOREDB_JWT_PUBLIC_KEY_PATH - Launching uvicorn
Do NOT set SPOREDB_JWT_SECRET_KEY_PATH or SPOREDB_JWT_PUBLIC_KEY_PATH to
local file paths in environment variables -- the files will not exist on the
ephemeral container filesystem. Use the base64 startup script pattern instead.
Cost Estimate¶
All prices are approximate monthly costs for DigitalOcean.
| Workload | App | Managed Postgres | Spaces | Total/month |
|---|---|---|---|---|
| Small (10 batches) | $5/mo (basic, 512 MB) | $7/mo (basic-256mb) | $5/mo (250 GB included) | ~$17/mo |
| Medium (50 batches) | $10/mo (basic, 1 GB) | $15/mo (basic-1gb) | $5/mo | ~$30/mo |
| Large (100+ batches) | $22/mo (pro, 1 GB) | $50/mo (basic-4gb) | $5/mo | ~$77/mo |
Recommended Sizing¶
| Workload | CPU | RAM | DB Plan | Spaces Storage |
|---|---|---|---|---|
| Small (10 batches) | 1 vCPU | 512 MB | basic-256mb | 10 GB |
| Medium (50 batches) | 1 vCPU | 1 GB | basic-1gb | 50 GB |
| Large (100+ batches) | 2 vCPU | 2 GB | basic-4gb | 100 GB |
To change app sizing, update the instance type in App Platform settings. To upgrade the database, modify the plan via the database component settings.
Troubleshooting¶
Build fails¶
- Verify the Dockerfile is at the repository root and the correct branch is selected
- Check the build logs in App Platform for specific errors
- Ensure the repository is connected and DigitalOcean has read access
Database connection refused / rfc1738 error¶
- Verify
SPOREDB_DATABASE_URLhas thepostgresql+asyncpg://prefix - Do NOT use the auto-injected
${db.DATABASE_URL}-- it usespostgresql://format which SporeDB does not auto-translate - Confirm the database is in the same region as the app and the trusted sources include the app component
- Ensure
?sslmode=requireis appended to the connection string (DigitalOcean managed databases require SSL)
Spaces access denied¶
- Verify the Spaces API key has read/write permissions
- Ensure the bucket region matches the endpoint URL (e.g.,
nyc3bucket useshttps://nyc3.digitaloceanspaces.com) - Check that the bucket name in
SPOREDB_S3_BUCKETmatches exactly
500 on auth endpoints / FileNotFoundError for keys¶
- Verify
SPOREDB_ED25519_PRIVATE_KEY_B64andSPOREDB_ED25519_PUBLIC_KEY_B64are set as encrypted environment variables - Confirm the Docker command override is in place (see Step 7)
- Check the app runtime logs for specific error messages
- Ensure the base64 encoding is correct: