Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions deploy/compose/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Buzz production Docker Compose environment.
# Copy to .env and replace every CHANGE_ME value before running.
# The bootstrap script should generate this file for normal users.

# Image published by the public image pipeline. Use `:main` for pre-release testing. Pin `:sha-<7>` or a semver release tag for production.
BUZZ_IMAGE=ghcr.io/block/buzz:main

# Public host name. Used by compose.caddy.yml and URL-derived settings below.
BUZZ_DOMAIN=buzz.example.com
RELAY_URL=wss://buzz.example.com
BUZZ_MEDIA_BASE_URL=https://buzz.example.com/media
BUZZ_MEDIA_SERVER_DOMAIN=buzz.example.com
BUZZ_CORS_ORIGINS=https://buzz.example.com

# Production defaults. Closed relay mode requires RELAY_OWNER_PUBKEY and a stable relay key.
BUZZ_REQUIRE_AUTH_TOKEN=true
BUZZ_REQUIRE_RELAY_MEMBERSHIP=true
BUZZ_ALLOW_NIP_OA_AUTH=true
BUZZ_AUTO_MIGRATE=true
BUZZ_GIT_CONFORMANCE_PROBE=true
RUST_LOG=buzz_relay=info,buzz_db=info,buzz_auth=info,buzz_pubsub=info,tower_http=info

# Owner identity. Set to a 64-character hex Nostr pubkey.
RELAY_OWNER_PUBKEY=CHANGE_ME_OWNER_PUBKEY_HEX

# Stable secrets. Generate once, keep in .env, and back up securely.
BUZZ_RELAY_PRIVATE_KEY=CHANGE_ME_64_HEX_PRIVATE_KEY
BUZZ_GIT_HOOK_HMAC_SECRET=CHANGE_ME_RANDOM_64_HEX
POSTGRES_DB=buzz
POSTGRES_USER=buzz
POSTGRES_PASSWORD=CHANGE_ME_RANDOM_PASSWORD
REDIS_PASSWORD=CHANGE_ME_RANDOM_PASSWORD
TYPESENSE_API_KEY=CHANGE_ME_RANDOM_API_KEY
BUZZ_S3_ACCESS_KEY=CHANGE_ME_RANDOM_ACCESS_KEY
BUZZ_S3_SECRET_KEY=CHANGE_ME_RANDOM_SECRET_KEY
BUZZ_S3_BUCKET=buzz-media

# Optional host ports. Base compose publishes the relay directly on BUZZ_HTTP_PORT.
BUZZ_HTTP_PORT=3000

# Caddy host ports. Only used with compose.caddy.yml.
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443

# Dev override ports. Only used with compose.dev.yml.
POSTGRES_PORT=5432
REDIS_PORT=6379
TYPESENSE_PORT=8108
MINIO_API_PORT=9000
MINIO_CONSOLE_PORT=9001
ADMINER_PORT=8082
PROMETHEUS_PORT=9090
5 changes: 5 additions & 0 deletions deploy/compose/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{$BUZZ_DOMAIN} {
encode zstd gzip

reverse_proxy relay:3000
}
57 changes: 57 additions & 0 deletions deploy/compose/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Buzz Docker Compose deployment

This is the single-node/VPS deployment bundle. It is intentionally separate from
the root `docker-compose.yml`, which remains local development infrastructure.

## Quick start

```bash
cd deploy/compose
cp .env.example .env
$EDITOR .env # replace every CHANGE_ME value
./run.sh start
```

For a public VPS with automatic Let's Encrypt certificates:

```bash
cd deploy/compose
BUZZ_COMPOSE_TLS=true ./run.sh start
```

The bootstrap script should eventually replace manual `.env` editing for normal
users. It is responsible for generating stable secrets and, optionally, an owner
keypair.

## Production notes

- Requires Docker Compose v2.24.4 or newer; the TLS override uses Compose's
`!reset` tag to remove the direct relay port when Caddy terminates HTTPS.
- Default `BUZZ_IMAGE` tracks `ghcr.io/block/buzz:main` for early testing. Pin it to `ghcr.io/block/buzz:sha-<7>` or a semver release tag for production once available.
- Keep `BUZZ_RELAY_PRIVATE_KEY`, `BUZZ_GIT_HOOK_HMAC_SECRET`, database/Redis,
Typesense, and S3 secrets stable across restarts.
- `RELAY_OWNER_PUBKEY` is intentionally not prefixed with `BUZZ_`; it must be a
64-character hex Nostr pubkey when closed relay mode is enabled.
- `BUZZ_AUTO_MIGRATE=true` requires an image that includes embedded SQLx
migrations. Do not share this quick start for a fresh public install until PR
#988 is merged and `ghcr.io/block/buzz:main` has been rebuilt from it. Before
then, this bundle is only suitable for instances whose database schema has
already been applied.
- The stack uses Postgres, Redis, Typesense, MinIO, and a git data volume because
those are real Buzz dependencies today. Minimal mode can simplify this later.

Run `./run.sh backup-hint` for the backup checklist.

## Validation

Before sharing an install link publicly, verify a fresh install with:

```bash
cd deploy/compose
cp .env.example .env
$EDITOR .env
./run.sh config
./run.sh start
curl -fsS "http://127.0.0.1:$(grep -E '^BUZZ_HTTP_PORT=' .env | cut -d= -f2-)/_liveness"
./run.sh status
```
29 changes: 29 additions & 0 deletions deploy/compose/compose.caddy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
relay:
ports: !reset []

caddy:
image: caddy:2-alpine
depends_on:
relay:
condition: service_healthy
environment:
BUZZ_DOMAIN: ${BUZZ_DOMAIN:?set BUZZ_DOMAIN}
ports:
- "${CADDY_HTTP_PORT:-80}:80"
- "${CADDY_HTTPS_PORT:-443}:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- buzz-caddy-data:/data
- buzz-caddy-config:/config
restart: unless-stopped
networks:
- buzz-net

volumes:
buzz-caddy-data:
labels:
com.buzz.volume: caddy-data
buzz-caddy-config:
labels:
com.buzz.volume: caddy-config
52 changes: 52 additions & 0 deletions deploy/compose/compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
services:
postgres:
ports:
- "${POSTGRES_PORT:-5432}:5432"

redis:
ports:
- "${REDIS_PORT:-6379}:6379"

typesense:
environment:
TYPESENSE_ENABLE_CORS: "true"
ports:
- "${TYPESENSE_PORT:-8108}:8108"

minio:
ports:
- "${MINIO_API_PORT:-9000}:9000"
- "${MINIO_CONSOLE_PORT:-9001}:9001"

adminer:
image: adminer:latest
container_name: buzz-adminer
depends_on:
postgres:
condition: service_healthy
environment:
ADMINER_DEFAULT_SERVER: postgres
ports:
- "${ADMINER_PORT:-8082}:8080"
restart: unless-stopped
networks:
- buzz-net

prometheus:
image: prom/prometheus:latest
container_name: buzz-prometheus
volumes:
- ../../prometheus.yml:/etc/prometheus/prometheus.yml:ro
- buzz-prometheus-data:/prometheus
ports:
- "${PROMETHEUS_PORT:-9090}:9090"
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
networks:
- buzz-net

volumes:
buzz-prometheus-data:
labels:
com.buzz.volume: prometheus
168 changes: 168 additions & 0 deletions deploy/compose/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
name: buzz-prod

services:
relay:
image: ${BUZZ_IMAGE:-ghcr.io/block/buzz:main}
env_file:
- .env
environment:
BUZZ_BIND_ADDR: 0.0.0.0:3000
BUZZ_HEALTH_PORT: "8080"
BUZZ_METRICS_PORT: "9102"
DATABASE_URL: postgres://${POSTGRES_USER:-buzz}:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-buzz}
REDIS_URL: redis://:${REDIS_PASSWORD:?set REDIS_PASSWORD}@redis:6379
TYPESENSE_URL: http://typesense:8108
BUZZ_S3_ENDPOINT: http://minio:9000
BUZZ_S3_ACCESS_KEY: ${BUZZ_S3_ACCESS_KEY:?set BUZZ_S3_ACCESS_KEY}
BUZZ_S3_SECRET_KEY: ${BUZZ_S3_SECRET_KEY:?set BUZZ_S3_SECRET_KEY}
BUZZ_S3_BUCKET: ${BUZZ_S3_BUCKET:-buzz-media}
BUZZ_GIT_REPO_PATH: /data/git
BUZZ_AUTO_MIGRATE: ${BUZZ_AUTO_MIGRATE:-true}
BUZZ_GIT_CONFORMANCE_PROBE: ${BUZZ_GIT_CONFORMANCE_PROBE:-true}
ports:
- "${BUZZ_HTTP_PORT:-3000}:3000"
volumes:
- buzz-git-data:/data/git
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
typesense:
condition: service_healthy
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
# Probe /_readiness over /dev/tcp because the runtime image has bash but no curl/wget/socat.
healthcheck:
test:
[
"CMD-SHELL",
"bash -ec 'exec 3<>/dev/tcp/127.0.0.1/8080; printf \"GET /_readiness HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\nConnection: close\\r\\n\\r\\n\" >&3; grep -q \"200 OK\" <&3'",
]
interval: 10s
timeout: 3s
retries: 12
start_period: 30s
restart: unless-stopped
networks:
- buzz-net

postgres:
image: postgres:18-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-buzz}
POSTGRES_USER: ${POSTGRES_USER:-buzz}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- buzz-postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
restart: unless-stopped
networks:
- buzz-net

redis:
image: redis:8-alpine
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD:?set REDIS_PASSWORD}"]
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD:?set REDIS_PASSWORD}
volumes:
- buzz-redis-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli -a \"$${REDIS_PASSWORD}\" ping | grep -q PONG"]
interval: 5s
timeout: 3s
retries: 12
start_period: 5s
restart: unless-stopped
networks:
- buzz-net

typesense:
image: typesense/typesense:30.2
environment:
TYPESENSE_DATA_DIR: /data
TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:?set TYPESENSE_API_KEY}
TYPESENSE_ENABLE_CORS: "false"
volumes:
- buzz-typesense-data:/data
healthcheck:
test:
[
"CMD-SHELL",
"bash -c 'exec 3<>/dev/tcp/127.0.0.1/8108; printf \"GET /health HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\nConnection: close\\r\\n\\r\\n\" >&3; cat <&3 | grep -q ok'",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 15s
restart: unless-stopped
networks:
- buzz-net

minio:
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${BUZZ_S3_ACCESS_KEY:?set BUZZ_S3_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${BUZZ_S3_SECRET_KEY:?set BUZZ_S3_SECRET_KEY}
volumes:
- buzz-minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
restart: unless-stopped
networks:
- buzz-net

minio-init:
image: minio/mc:RELEASE.2025-08-13T08-35-41Z
depends_on:
minio:
condition: service_healthy
environment:
BUZZ_S3_ACCESS_KEY: ${BUZZ_S3_ACCESS_KEY:?set BUZZ_S3_ACCESS_KEY}
BUZZ_S3_SECRET_KEY: ${BUZZ_S3_SECRET_KEY:?set BUZZ_S3_SECRET_KEY}
BUZZ_S3_BUCKET: ${BUZZ_S3_BUCKET:-buzz-media}
entrypoint: >
/bin/sh -euc '
mc alias set local http://minio:9000 "$${BUZZ_S3_ACCESS_KEY}" "$${BUZZ_S3_SECRET_KEY}"
mc mb --ignore-existing "local/$${BUZZ_S3_BUCKET}"
mc anonymous set none "local/$${BUZZ_S3_BUCKET}"
'
restart: "no"
networks:
- buzz-net

volumes:
buzz-postgres-data:
labels:
com.buzz.volume: postgres
buzz-redis-data:
labels:
com.buzz.volume: redis
buzz-typesense-data:
labels:
com.buzz.volume: typesense
buzz-minio-data:
labels:
com.buzz.volume: minio
buzz-git-data:
labels:
com.buzz.volume: git

networks:
buzz-net:
driver: bridge
labels:
com.buzz.network: production
Loading