> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blindcast.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Setup

> Run BlindCast Server with Docker Compose — Postgres, MinIO (S3), and the server in a single stack. Bring your own database or storage.

The server ships with a Docker Compose file that bundles Postgres, MinIO (S3-compatible), and the BlindCast server.

## Quick start

```bash theme={null}
cd packages/server

# Generate required secrets
export MASTER_KEY_HEX=$(openssl rand -hex 32)
export SALT_HEX=$(openssl rand -hex 32)

# Start all services
docker compose up -d
```

The stack is healthy when all three services are running:

```bash theme={null}
docker compose ps
# NAME        STATUS
# postgres    healthy
# minio       healthy
# blindcast   healthy
```

Open `http://localhost:4100/admin` to access the admin dashboard.

## Environment variables

### Required

| Variable         | Description                   | Example                |
| ---------------- | ----------------------------- | ---------------------- |
| `MASTER_KEY_HEX` | Master key as hex (32+ bytes) | `openssl rand -hex 32` |
| `SALT_HEX`       | Salt as hex (32 bytes)        | `openssl rand -hex 32` |

### Database

| Variable            | Default              | Description                   |
| ------------------- | -------------------- | ----------------------------- |
| `DATABASE_URL`      | Bundled Postgres     | PostgreSQL connection string  |
| `POSTGRES_PASSWORD` | `blindcast-dev-only` | Password for bundled Postgres |

### S3 / Storage

| Variable                | Default              | Description                                   |
| ----------------------- | -------------------- | --------------------------------------------- |
| `S3_BUCKET`             | `blindcast-segments` | S3 bucket name. Setting this enables presign. |
| `S3_ENDPOINT`           | `http://minio:9000`  | S3 endpoint URL (for MinIO/R2)                |
| `S3_REGION`             | `us-east-1`          | AWS region                                    |
| `AWS_ACCESS_KEY_ID`     | `blindcast`          | S3 access key                                 |
| `AWS_SECRET_ACCESS_KEY` | `blindcast-dev-only` | S3 secret key                                 |

### Authentication

| Variable            | Default | Description                                  |
| ------------------- | ------- | -------------------------------------------- |
| `CORS_ORIGINS`      | `*`     | Allowed CORS origins (comma-separated)       |
| `ADMIN_API_KEY`     | —       | Bootstrap admin API key (skips setup wizard) |
| `AUTH_JWT_SECRET`   | —       | HS256 secret for viewer JWT auth             |
| `AUTH_JWKS_URL`     | —       | JWKS URL for RS256/ES256 viewer auth         |
| `AUTH_JWT_AUDIENCE` | —       | Expected JWT audience claim                  |

### Optional

| Variable         | Default | Description                                                                                                  |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `PORT`           | `4100`  | Server listen port                                                                                           |
| `LEASE_TTL_MS`   | —       | Lease duration in ms (requires auth)                                                                         |
| `ENABLE_PRESIGN` | Auto    | Explicitly enable/disable presign (`true`/`false`)                                                           |
| `TRUST_PROXY`    | `false` | Trust proxy headers (`true`, `loopback`, IP/CIDR, or hop count). See [Reverse Proxy](/server/reverse-proxy). |

## Bring your own Postgres

To use an external Postgres instance, set `DATABASE_URL` and remove the `postgres` service from `docker-compose.yml`:

```yaml theme={null}
services:
  blindcast:
    # ...
    environment:
      DATABASE_URL: postgresql://user:password@your-host:5432/blindcast
```

The server creates tables automatically on startup (`CREATE TABLE IF NOT EXISTS`).

## Bring your own S3

Replace MinIO with any S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2):

```yaml theme={null}
services:
  blindcast:
    environment:
      S3_BUCKET: your-bucket
      S3_REGION: us-west-2
      AWS_ACCESS_KEY_ID: AKIA...
      AWS_SECRET_ACCESS_KEY: ...
      # Omit S3_ENDPOINT to use real AWS S3
      # For R2: S3_ENDPOINT=https://<account>.r2.cloudflarestorage.com
```

## Production considerations

<Warning>
  The default `docker-compose.yml` uses dev-only passwords. For production:

  1. Set strong `POSTGRES_PASSWORD` and `AWS_SECRET_ACCESS_KEY` values
  2. Use a managed Postgres instance (RDS, Cloud SQL, etc.)
  3. Set `CORS_ORIGINS` to your specific domain(s)
  4. Configure viewer auth (`AUTH_JWT_SECRET` or `AUTH_JWKS_URL`)
  5. Put a reverse proxy in front of BlindCast for TLS and admin protection. See [Reverse Proxy](/server/reverse-proxy).
</Warning>

### Persistent data

The Compose file defines three named volumes:

| Volume           | Purpose                 |
| ---------------- | ----------------------- |
| `postgres-data`  | Postgres database files |
| `minio-data`     | MinIO object storage    |
| `blindcast-data` | Server data directory   |

### Health checks

All services include health checks:

* **Postgres**: `pg_isready`
* **MinIO**: `GET /minio/health/live`
* **BlindCast**: `GET /health`

## Building from source

```bash theme={null}
# From the repository root
docker compose -f packages/server/docker-compose.yml build
```

The multi-stage Dockerfile builds `@blindcast/crypto`, `@blindcast/keys`, `@blindcast/storage`, and `@blindcast/server` in sequence, then creates a minimal production image.

## Next steps

* [Content API](/server/content-api) — register and manage content
* [API Keys](/server/api-keys) — create and manage API keys
* [Admin Dashboard](/server/admin-dashboard) — browser-based management
