Documentation Developers
Docker deployment
Pull the private x86-64 image and deploy the app, worker and database on your server.
On this page
What gets deployed
The private repository is bcsutar/gridpreview. GitHub Actions builds an x86-64 (linux/amd64) image and publishes it to the private ghcr.io/bcsutar/gridpreview package. Each successful publication produces a latest tag and an immutable commit-SHA tag. The workflow verifies that the registry package is private.
compose.production.yml runs four services:
| Service | Responsibility |
|---|---|
postgres | PostgreSQL 17 with a persistent named volume; no published database port |
initialize | One-shot schema migrations and safe default configuration seeding |
web | Production Next.js server, bound to the host’s loopback interface |
worker | Durable research execution using the same application image |
The application containers run as a non-root user. Web and worker start only after initialization succeeds. Provider credentials and session secrets are supplied at runtime, never during the image build.
Prepare the x86 server
Install Docker Engine and the Docker Compose v2 plugin. Use an authorized SSH key or other private-repository credential to obtain the deployment files:
git clone [email protected]:bcsutar/gridpreview.git
cd gridpreview
cp .env.production.example .env
chmod 600 .env
Edit .env. Generate four independent values for POSTGRES_PASSWORD, AUTH_COOKIE_SECRET, RESEARCH_ACCESS_TOKEN and LOCAL_ADMIN_TOKEN:
openssl rand -hex 32
Use hexadecimal for the database password because it is embedded in the PostgreSQL connection URL. Set the provider key required by your published model configuration. The seeded default uses OpenCode Go Muse Spark Contributor; verify its workload and data-sharing permissions or publish a different supported provider/model before using it.
Keep grid-source access disabled until reuse permission is established. Do not copy a development database or local credentials unless you explicitly intend to migrate them.
Authenticate to the private registry
Use a GitHub credential with read:packages access to this private package. A classic personal access token with that scope is supported; repository access alone is not necessarily registry access. Store the credential through Docker’s credential handling, not in Compose or a committed file.
docker login ghcr.io --username bcsutar
Enter the package-read token at the password prompt. Select an immutable published image tag in .env for predictable deployments:
GRIDPREVIEW_IMAGE=ghcr.io/bcsutar/gridpreview:<published-commit-sha>
APP_PORT=3000
The placeholder is not a real image tag. Copy the commit tag from a successful Private container workflow run. latest is available for convenience but changes when a new image is published.
Start and inspect
docker compose -f compose.production.yml config --quiet
docker compose -f compose.production.yml pull
docker compose -f compose.production.yml up -d --wait
docker compose -f compose.production.yml ps -a
docker compose -f compose.production.yml logs --tail=100 initialize web worker
initialize should exit successfully. PostgreSQL and web have health checks; a running worker container does not by itself prove that every external provider is configured or available. Container startup does not create a research run or validate a model-backed report.
Do not share unredacted environment/configuration dumps. docker compose config without --quiet can print interpolated credentials.
Put HTTPS in front
The web port binds to 127.0.0.1:3000 by default. Put your existing HTTPS reverse proxy in front; do not expose PostgreSQL. An Nginx upstream location should preserve the public host and avoid buffering research events:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_read_timeout 360s;
}
This location snippet assumes an existing TLS virtual host and certificate. Production owner cookies are Secure; use HTTPS for browser research sessions. Do not remove authentication to work around a proxy or cookie issue.
Establish access
Public product and documentation pages do not authorize research. On the HTTPS site, use POST /api/session with your RESEARCH_ACCESS_TOKEN to issue a signed owner cookie; keep that cookie for subsequent requests. See API reference for the request format. Admin API access separately requires LOCAL_ADMIN_TOKEN as a bearer credential.
Access is a private capability-based prototype, not a public signup system. Do not publish access tokens, add them to URLs or assume that two independently issued sessions share an owner.
Back up before updates
Keep database backups outside the application repository and copy them off the server. Back up .env securely as well, especially the cookie-signing secret.
mkdir -p backups
chmod 700 backups
docker compose -f compose.production.yml exec -T postgres \
pg_dump -U grid -d gridpreview -Fc > backups/gridpreview.dump
chmod 600 backups/gridpreview.dump
A backup is not verified until restored. To test restoration, use a separate fresh deployment with an empty database, start only its PostgreSQL service, and restore before running initialization:
docker compose -f compose.production.yml up -d --wait postgres
docker compose -f compose.production.yml exec -T postgres \
pg_restore -U grid -d gridpreview --no-owner < backups/gridpreview.dump
Do not run this restore against an existing live database. Keep a matching application image tag with the backup.
Pull an update
Obtain the updated Compose files when needed and change GRIDPREVIEW_IMAGE to the new immutable tag. During a maintenance window:
docker compose -f compose.production.yml pull
docker compose -f compose.production.yml stop web worker
docker compose -f compose.production.yml run --rm initialize
docker compose -f compose.production.yml up -d --wait
The worker handles termination by finishing its current job and then exiting without claiming another. Compose allows 330 seconds for this, longer than the default 300-second run budget. Increasing the run budget requires a correspondingly longer shutdown grace period.
Never use docker compose down -v for an update: it removes the database volume. Application rollback does not automatically reverse database migrations; assess compatibility or restore a matching backup before rolling back a migrated deployment.