Skip to main content

Docker Deployment

Deploy Steward with Docker Compose for a production-ready setup with PostgreSQL, the Steward API, and the proxy gateway.

Docker Compose

Create a docker-compose.yml:
version: "3.8"

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: steward
      POSTGRES_USER: steward
      POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U steward"]
      interval: 5s
      timeout: 5s
      retries: 5

  steward-api:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgres://steward:${DB_PASSWORD:-changeme}@postgres:5432/steward
      STEWARD_MASTER_PASSWORD: ${STEWARD_MASTER_PASSWORD}
      STEWARD_JWT_SECRET: ${STEWARD_JWT_SECRET}
      STEWARD_PORT: "3200"
      STEWARD_BIND_HOST: "0.0.0.0"
      RPC_URL: ${RPC_URL:-https://mainnet.base.org}
      CHAIN_ID: ${CHAIN_ID:-8453}
      SOLANA_RPC_URL: ${SOLANA_RPC_URL:-https://api.mainnet-beta.solana.com}
      STEWARD_PLATFORM_KEY: ${STEWARD_PLATFORM_KEY}
    ports:
      - "3200:3200"
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

volumes:
  pgdata:

Environment File

Create a .env file:
# Database
DB_PASSWORD=your-strong-db-password

# Steward (REQUIRED)
STEWARD_MASTER_PASSWORD=your-very-strong-master-password-32-chars-min
STEWARD_JWT_SECRET=separate-jwt-signing-secret

# Blockchain
RPC_URL=https://mainnet.base.org
CHAIN_ID=8453
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com

# Platform admin (optional)
STEWARD_PLATFORM_KEY=your-platform-admin-key

Start

docker compose up -d

# Check logs
docker compose logs -f steward-api

# Verify health
curl http://localhost:3200/health

Database Migrations

Run migrations after the first start:
docker compose exec steward-api bun run drizzle-kit push

Adding Agent Containers

To run agent containers alongside Steward on the same Docker network:
# Add to docker-compose.yml
services:
  # ... existing services ...

  my-agent:
    image: your-agent-image:latest
    environment:
      STEWARD_API_URL: http://steward-api:3200
      STEWARD_AGENT_TOKEN: ${AGENT_TOKEN}
      STEWARD_AGENT_ID: my-agent
    depends_on:
      - steward-api
    networks:
      - default

Network Isolation

For maximum security, create a separate network where agent containers can only reach the Steward proxy:
networks:
  steward-internal:
    driver: bridge
  agents-isolated:
    driver: bridge
    internal: true  # No internet access

services:
  steward-api:
    networks:
      - steward-internal

  steward-proxy:
    networks:
      - steward-internal
      - agents-isolated  # Bridge between agents and Steward

  my-agent:
    networks:
      - agents-isolated  # Can ONLY reach steward-proxy
With network isolation, agents cannot make any network requests except to the Steward proxy. The proxy handles all outbound traffic, injecting credentials and enforcing policies.

Updating

# Pull latest
git pull origin develop

# Rebuild
docker compose build steward-api

# Restart with zero downtime
docker compose up -d steward-api

Backup

# Database backup
docker compose exec postgres pg_dump -U steward steward > backup.sql

# Restore
cat backup.sql | docker compose exec -T postgres psql -U steward steward
Always back up your database before updates. The database contains encrypted keys — if lost, all agent wallets are inaccessible.