mirror of
https://github.com/block/buzz.git
synced 2026-11-01 23:11:58 +00:00
Add Railway deployment template
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
parent
6541765416
commit
365c1e11f2
59
deploy/railway/README.md
Normal file
59
deploy/railway/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Buzz on Railway
|
||||
|
||||
This directory contains Buzz's first-party Railway template draft. It is intentionally scoped to Railway only; the repository README deploy button should be wired after the public image and automatic migrations PRs land.
|
||||
|
||||
```markdown
|
||||
[](https://railway.com/deploy/buzz?referralCode=buzz)
|
||||
```
|
||||
|
||||
## Template shape
|
||||
|
||||
`railway.template.json` models the current full Buzz deployment as six Railway services:
|
||||
|
||||
- `Buzz` (`ghcr.io/block/buzz:latest`) — the relay and bundled web UI.
|
||||
- `Postgres` — persistent database volume with daily backups requested in the template.
|
||||
- `Redis` — authenticated persistent pub/sub/cache service.
|
||||
- `Typesense` — persistent search service.
|
||||
- `MinIO` — S3-compatible media/object storage.
|
||||
- `MinIO Init` — idempotently creates the `buzz-media` bucket.
|
||||
|
||||
The only required user input is `RELAY_OWNER_PUBKEY`, a 64-character hex Nostr public key. Railway generates the relay key, database password, Redis password, Typesense key, MinIO password, and git hook HMAC secret with `${{secret(...)}}`.
|
||||
|
||||
## Buzz-specific defaults
|
||||
|
||||
The template uses the environment names read by `crates/buzz-relay/src/config.rs`. The app router itself exposes `/_readiness`, so Railway can health-check the public service without using Buzz's separate health-only port:
|
||||
|
||||
- `RELAY_URL=wss://${{RAILWAY_PUBLIC_DOMAIN}}`
|
||||
- `RELAY_OWNER_PUBKEY=<required user input>`
|
||||
- `DATABASE_URL=${{Postgres.DATABASE_URL}}`
|
||||
- `REDIS_URL=${{Redis.REDIS_URL}}`
|
||||
- `TYPESENSE_URL=http://${{Typesense.RAILWAY_PRIVATE_DOMAIN}}:${{Typesense.PORT}}`
|
||||
- `TYPESENSE_API_KEY=${{Typesense.TYPESENSE_API_KEY}}`
|
||||
- `BUZZ_S3_*` from the MinIO service; MinIO also sets `MINIO_DOMAIN=${{RAILWAY_PRIVATE_DOMAIN}}` so path-style S3 requests work on Railway private networking.
|
||||
- `BUZZ_GIT_REPO_PATH=/data/git`
|
||||
- `BUZZ_REQUIRE_AUTH_TOKEN=true`
|
||||
- `BUZZ_REQUIRE_RELAY_MEMBERSHIP=true`
|
||||
- `BUZZ_ALLOW_NIP_OA_AUTH=true`
|
||||
|
||||
Railway terminates TLS, so the public relay URL is `wss://` and CORS/media URLs use `https://`.
|
||||
|
||||
## Validation status
|
||||
|
||||
This template is mechanically valid JSON and follows the v2 template mechanics observed in Railway's Plausible, NocoDB, and Typesense templates: service refs, generated secrets, `serviceDomains`, `volumeMounts`, and app health checks.
|
||||
|
||||
End-to-end click-through validation is intentionally blocked until:
|
||||
|
||||
1. `ghcr.io/block/buzz:<version>` is publicly published.
|
||||
2. Buzz owns fresh-database migrations at startup or through the same image.
|
||||
|
||||
Until both land, this template should be treated as first-party deploy wiring, not a proven production install.
|
||||
|
||||
## Operational notes for users
|
||||
|
||||
Back up these values and volumes before relying on a Railway deployment:
|
||||
|
||||
- The owner private key corresponding to `RELAY_OWNER_PUBKEY` — Railway only stores the public key.
|
||||
- `BUZZ_RELAY_PRIVATE_KEY` and `BUZZ_GIT_HOOK_HMAC_SECRET`.
|
||||
- Postgres data.
|
||||
- MinIO media/object data.
|
||||
- The Buzz `/data` volume that stores git name-reservation state.
|
||||
187
deploy/railway/railway.template.json
Normal file
187
deploy/railway/railway.template.json
Normal file
@@ -0,0 +1,187 @@
|
||||
{
|
||||
"$schema": "https://railway.com/railway.schema.json",
|
||||
"name": "Buzz",
|
||||
"description": "Self-host a Buzz Nostr relay with Postgres, Redis, Typesense, and S3-compatible media storage.",
|
||||
"services": {
|
||||
"buzz": {
|
||||
"name": "Buzz",
|
||||
"source": {
|
||||
"image": "ghcr.io/block/buzz:latest"
|
||||
},
|
||||
"deploy": {
|
||||
"healthcheckPath": "/_readiness",
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
},
|
||||
"networking": {
|
||||
"serviceDomains": {
|
||||
"buzz": {}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"BUZZ_BIND_ADDR": "0.0.0.0:${{PORT}}",
|
||||
"BUZZ_METRICS_PORT": "9102",
|
||||
"BUZZ_RELAY_PRIVATE_KEY": "${{secret(64, 'abcdef0123456789')}}",
|
||||
"BUZZ_REQUIRE_AUTH_TOKEN": "true",
|
||||
"BUZZ_REQUIRE_RELAY_MEMBERSHIP": "true",
|
||||
"BUZZ_ALLOW_NIP_OA_AUTH": "true",
|
||||
"BUZZ_CORS_ORIGINS": "https://${{RAILWAY_PUBLIC_DOMAIN}}",
|
||||
"BUZZ_GIT_REPO_PATH": "/data/git",
|
||||
"BUZZ_GIT_HOOK_HMAC_SECRET": "${{secret(64, 'abcdef0123456789')}}",
|
||||
"BUZZ_GIT_CONFORMANCE_PROBE": "true",
|
||||
"BUZZ_S3_ENDPOINT": "http://${{MinIO.RAILWAY_PRIVATE_DOMAIN}}:${{MinIO.PORT}}",
|
||||
"BUZZ_S3_ACCESS_KEY": "${{MinIO.MINIO_ROOT_USER}}",
|
||||
"BUZZ_S3_SECRET_KEY": "${{MinIO.MINIO_ROOT_PASSWORD}}",
|
||||
"BUZZ_S3_BUCKET": "buzz-media",
|
||||
"BUZZ_MEDIA_BASE_URL": "https://${{RAILWAY_PUBLIC_DOMAIN}}/media",
|
||||
"DATABASE_URL": "${{Postgres.DATABASE_URL}}",
|
||||
"REDIS_URL": "${{Redis.REDIS_URL}}",
|
||||
"RELAY_OWNER_PUBKEY": {
|
||||
"description": "Required: 64-character hex Nostr public key for the relay owner. Generate or back up the matching private key outside Railway.",
|
||||
"isOptional": false
|
||||
},
|
||||
"RELAY_URL": "wss://${{RAILWAY_PUBLIC_DOMAIN}}",
|
||||
"TYPESENSE_API_KEY": "${{Typesense.TYPESENSE_API_KEY}}",
|
||||
"TYPESENSE_URL": "http://${{Typesense.RAILWAY_PRIVATE_DOMAIN}}:${{Typesense.PORT}}"
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"mountPath": "/data",
|
||||
"volumeName": "buzz-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"postgres": {
|
||||
"name": "Postgres",
|
||||
"source": {
|
||||
"image": "ghcr.io/railwayapp-templates/postgres-ssl:17"
|
||||
},
|
||||
"deploy": {
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
},
|
||||
"variables": {
|
||||
"PGDATA": "/var/lib/postgresql/data",
|
||||
"POSTGRES_DB": "buzz",
|
||||
"POSTGRES_PASSWORD": "${{secret(32)}}",
|
||||
"POSTGRES_USER": "buzz"
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"mountPath": "/var/lib/postgresql/data",
|
||||
"volumeName": "postgres-data",
|
||||
"backupSchedules": [
|
||||
"DAILY"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"redis": {
|
||||
"name": "Redis",
|
||||
"source": {
|
||||
"image": "redis:8-alpine"
|
||||
},
|
||||
"deploy": {
|
||||
"startCommand": "/bin/sh -c \"rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found && exec redis-server --requirepass $REDIS_PASSWORD --appendonly yes --dir $RAILWAY_VOLUME_MOUNT_PATH\"",
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
},
|
||||
"variables": {
|
||||
"REDIS_PASSWORD": "${{secret(32)}}",
|
||||
"REDIS_URL": "redis://:${{REDIS_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:6379"
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"mountPath": "/data",
|
||||
"volumeName": "redis-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"typesense": {
|
||||
"name": "Typesense",
|
||||
"source": {
|
||||
"image": "typesense/typesense:30.2"
|
||||
},
|
||||
"deploy": {
|
||||
"startCommand": "typesense-server --data-dir $RAILWAY_VOLUME_MOUNT_PATH --api-key $TYPESENSE_API_KEY --enable-cors",
|
||||
"healthcheckPath": "/health",
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
},
|
||||
"variables": {
|
||||
"PORT": "8108",
|
||||
"TYPESENSE_API_KEY": "${{secret(32)}}"
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"mountPath": "/data",
|
||||
"volumeName": "typesense-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minio": {
|
||||
"name": "MinIO",
|
||||
"source": {
|
||||
"image": "minio/minio:RELEASE.2025-09-07T16-13-09Z"
|
||||
},
|
||||
"deploy": {
|
||||
"startCommand": "server $RAILWAY_VOLUME_MOUNT_PATH --address :$PORT",
|
||||
"healthcheckPath": "/minio/health/live",
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
},
|
||||
"variables": {
|
||||
"MINIO_BROWSER": "off",
|
||||
"MINIO_ROOT_PASSWORD": "${{secret(32)}}",
|
||||
"MINIO_ROOT_USER": "buzz",
|
||||
"PORT": "9000",
|
||||
"MINIO_DOMAIN": "${{RAILWAY_PRIVATE_DOMAIN}}"
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"mountPath": "/data",
|
||||
"volumeName": "minio-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minio-init": {
|
||||
"name": "MinIO Init",
|
||||
"source": {
|
||||
"image": "minio/mc:RELEASE.2025-08-13T08-35-41Z"
|
||||
},
|
||||
"deploy": {
|
||||
"startCommand": "/bin/sh -c \"until mc alias set local http://${{MinIO.RAILWAY_PRIVATE_DOMAIN}}:${{MinIO.PORT}} ${{MinIO.MINIO_ROOT_USER}} ${{MinIO.MINIO_ROOT_PASSWORD}}; do sleep 2; done; mc mb --ignore-existing local/buzz-media; mc anonymous set none local/buzz-media\"",
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
"canvasConfig": {
|
||||
"services": {
|
||||
"buzz": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"postgres": {
|
||||
"x": -280,
|
||||
"y": 180
|
||||
},
|
||||
"redis": {
|
||||
"x": 0,
|
||||
"y": 180
|
||||
},
|
||||
"typesense": {
|
||||
"x": 280,
|
||||
"y": 180
|
||||
},
|
||||
"minio": {
|
||||
"x": 560,
|
||||
"y": 180
|
||||
},
|
||||
"minio-init": {
|
||||
"x": 560,
|
||||
"y": 360
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user