Docker Configuration
Docker serves two purposes in SystemPrompt: running PostgreSQL locally and deploying your application to production.
On this page
Docker serves two distinct purposes in SystemPrompt: running PostgreSQL for local development and deploying your application to production. Understanding which Docker files to use and when is essential for a smooth development workflow.
Playbook: For machine-executable commands and quick reference, see the Cloud Management Playbook.
PostgreSQL Containers
SystemPrompt uses Docker to run PostgreSQL locally. You have two options depending on your setup: a shared container for multiple projects, or tenant-specific containers for isolated development.
Shared PostgreSQL
The shared PostgreSQL container allows multiple SystemPrompt projects to share a single database server. This is efficient when you're working on several projects simultaneously and want to minimize resource usage.
The configuration lives in .systemprompt/docker/shared.yaml. When you run just db-up, this container starts and exposes PostgreSQL on port 5432. A companion file, shared_config.json, tracks which tenant databases exist within this shared server.
# Start shared PostgreSQL
just db-up
# Stop shared PostgreSQL
just db-down
# Reset shared PostgreSQL (removes all data)
just db-reset
# View PostgreSQL logs
just db-logs
# List all tenant databases in shared server
just db-list
Each tenant gets its own database within the shared server. When you create a new local tenant with systemprompt cloud tenant create, it automatically registers a database in the shared configuration. Your profile's DATABASE_URL in secrets.json points to this specific database.
Tenant-Specific PostgreSQL
For isolated development, each tenant can have its own dedicated PostgreSQL container. This is useful when you need different PostgreSQL versions or configurations per project, or when you want complete isolation between environments.
Tenant-specific configurations are generated automatically in .systemprompt/docker/{TENANT_NAME}.yaml when you create a tenant. Each container uses a named volume to persist data across restarts.
# Start a specific tenant's PostgreSQL
just db-up TENANT="my-project"
# Stop a specific tenant's PostgreSQL
just db-down TENANT="my-project"
The tenant-specific approach uses more resources but provides complete isolation. Changes to one project's database cannot affect another.
Application Deployment
Beyond database containers, Docker is used to package your SystemPrompt application for deployment. Profile-specific Dockerfiles define how your application is built and run in production.
Profile Dockerfiles
Each profile can include its own Dockerfile in .systemprompt/profiles/{profile-name}/docker/. This allows different deployment configurations for development versus production.
A local profile Dockerfile might include debugging tools and skip certain optimizations. A production profile Dockerfile focuses on minimal image size, security hardening, and proper health checks.
The Dockerfile structure follows a consistent pattern:
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
ca-certificates libssl3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY target/release/systemprompt /app/bin/
COPY services/ /app/services/
COPY web/ /app/web/
ENV HOST=0.0.0.0 PORT=8080
EXPOSE 8080
CMD ["/app/bin/systemprompt", "infra", "services", "start", "--all"]
Entrypoint Scripts
Each profile includes an entrypoint.sh script that runs before your application starts. This typically handles database migrations and any pre-flight checks:
#!/bin/sh
set -e
echo "Running database migrations..."
/app/bin/systemprompt infra db migrate
echo "Starting services..."
exec /app/bin/systemprompt infra services serve --foreground
The entrypoint ensures your database schema is up-to-date before the application accepts traffic.
Building Images Locally
Test your Docker build locally before deploying to production:
# Build the release binary first
cargo build --release -p systemprompt-cli
# Build MCP servers if using them
just build-mcp
# Build Docker image
just docker-build local
# Run the image locally
just docker-run local
This workflow validates that your Dockerfile works correctly and your application starts as expected.
Cloud Deployment
When you run just deploy, the CLI orchestrates the full deployment workflow:
- Builds the Rust binary with release optimizations
- Builds any MCP servers your application uses
- Uses the active profile's Dockerfile to build an image
- Pushes the image to the container registry
- Deploys to your cloud tenant
# Full deployment workflow
just deploy
# Deploy a specific profile
systemprompt cloud deploy --profile production --yes
# Redeploy without rebuilding (uses existing image)
systemprompt cloud deploy --profile production --yes --skip-push
The deployment process respects your profile's Dockerfile, ensuring environment-specific configurations are correctly applied.
Environment Variables
Docker containers receive configuration through environment variables. These override defaults and connect your application to external services.
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
HOST |
Bind address (default: 0.0.0.0) | No |
PORT |
Server port (default: 8080) | No |
RUST_LOG |
Log level (info, debug, trace) | No |
SYSTEMPROMPT_PROFILE |
Path to active profile | No |
SYSTEMPROMPT_SERVICES_PATH |
Path to services directory | No |
Pass environment variables when running containers:
docker run -p 8080:8080 \
-e DATABASE_URL="postgres://user:pass@host:5432/db" \
-e RUST_LOG="info" \
myproject:latest
Docker Compose for Development
For local development with both the application and PostgreSQL, use Docker Compose:
# docker-compose.yaml
services:
app:
build: .
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://systemprompt:localdev@postgres:5432/systemprompt
depends_on:
- postgres
postgres:
image: postgres:18-alpine
environment:
POSTGRES_USER: systemprompt
POSTGRES_PASSWORD: localdev
POSTGRES_DB: systemprompt
volumes:
- pgdata:/var/lib/postgresql
volumes:
pgdata:
docker compose up -d
This approach bundles everything needed to run your application in a single command.
Production Considerations
Multi-stage Builds
Reduce image size with multi-stage builds that separate compilation from runtime:
# Build stage
FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release -p systemprompt-cli
# Runtime stage
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates libssl3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/systemprompt /app/bin/
COPY services/ /app/services/
COPY web/ /app/web/
ENV HOST=0.0.0.0 PORT=8080
EXPOSE 8080
CMD ["/app/bin/systemprompt", "infra", "services", "start", "--all"]
Health Checks
Add health checks so orchestrators can detect unhealthy containers:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/api/v1/health || exit 1
Secrets Management
Never bake secrets into Docker images. Pass them at runtime through environment variables or a secrets manager:
# Environment variable (simple)
docker run -e DATABASE_URL="$DATABASE_URL" myproject:latest
# Docker secrets (Swarm mode)
docker secret create db_url ./db_url.txt
docker service create --secret db_url myproject:latest
Directory Structure
Docker-related files are organized within .systemprompt/:
.systemprompt/
├── Dockerfile # Template for local testing
├── .dockerignore # Files excluded from build context
├── entrypoint.sh # Root entrypoint script
├── docker/
│ ├── shared.yaml # Shared PostgreSQL container
│ ├── shared_config.json # Tracks databases in shared server
│ └── {TENANT}.yaml # Tenant-specific PostgreSQL
└── profiles/
├── local/
│ └── docker/
│ ├── Dockerfile # Local profile build
│ ├── Dockerfile.dockerignore
│ └── entrypoint.sh
└── production/
└── docker/
├── Dockerfile # Production profile build
├── Dockerfile.dockerignore
└── entrypoint.sh
Troubleshooting
Password Authentication Failed
Symptom: After running systemprompt cloud tenant, you see "password authentication failed for user 'systemprompt_admin'".
Cause: PostgreSQL stores credentials in its data volume during initial setup. If the volume already exists with a different password than your configuration, authentication fails.
Solutions:
-
Multi-project setup: If the container was created by another project, get the password from that project's config:
cat /path/to/other-project/.systemprompt/docker/shared_config.jsonThen run
systemprompt cloud tenantagain and choose "Add tenant to existing container" when prompted. -
Reset everything (destructive - deletes all databases):
docker compose -f .systemprompt/docker/shared.yaml down -v docker volume rm systemprompt-postgres-shared-data rm .systemprompt/docker/shared_config.json systemprompt cloud tenant
Container Running But No Local Config
Symptom: When creating a tenant, you see "Shared PostgreSQL container is running but no local configuration found."
Cause: Another SystemPrompt project created the shared container. Your project doesn't have the password.
Solution: Choose one of:
- "Add tenant to existing container" - Enter the password from the other project's
shared_config.json - "Cancel" - Stop the existing container first:
docker stop systemprompt-postgres-shared
Orphaned Volume
Symptom: "PostgreSQL data volume exists but no container or configuration found."
Cause: Container was removed but the volume persists with old credentials.
Solution: Either reset the volume when prompted, or remove it manually:
docker volume rm systemprompt-postgres-shared-data
Connection Refused
Symptom: Cannot connect to PostgreSQL on localhost:5432.
Solutions:
- Check if container is running:
docker ps -f name=systemprompt-postgres-shared - Start the container:
docker compose -f .systemprompt/docker/shared.yaml up -d - Wait for health check:
docker inspect --format='{{.State.Health.Status}}' systemprompt-postgres-shared
Quick Reference
| Task | Command |
|---|---|
| Start shared PostgreSQL | just db-up |
| Stop shared PostgreSQL | just db-down |
| Reset shared PostgreSQL | just db-reset |
| View PostgreSQL logs | just db-logs |
| Start tenant PostgreSQL | just db-up TENANT="name" |
| Build Docker image | just docker-build local |
| Run Docker image | just docker-run local |
| Deploy to cloud | just deploy |
| View profile Dockerfile | systemprompt cloud dockerfile |
| Check container status | docker ps -f name=systemprompt-postgres-shared |
| Check volume exists | docker volume ls -f name=systemprompt-postgres-shared-data |
| View password | cat .systemprompt/docker/shared_config.json | jq -r '.admin_password' |