sim Docker & Self-Hosted Deployment

GitHub

Sim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.

RAW Compose
1. Pull Image (DOCKERHUB) docker pull library/redis
2. Docker Run (1-Click Launch) docker run -d --name sim -p 3000:3000 --restart always redis:7-alpine
3. Podman Run podman run -d --name sim -p 3000:3000 --restart always redis:7-alpine
4. Skopeo Inspect (Zero-Pull Manifest) skopeo inspect docker://docker.io/library/redis:7-alpine
5. ORAS Pull (OCI Artifact) oras pull redis:7-alpine
6. Compose Deployment docker compose up -d
docker-compose.local.yml
Download .yml
services:
  simstudio:
    env_file:
      - path: .env
        required: false
    build:
      context: .
      dockerfile: docker/app.Dockerfile
    ports:
      - '3000:3000'
    deploy:
      resources:
        limits:
          memory: 8G
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
      - BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
      - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-dev-secret-at-least-32-characters-long}
      - ENCRYPTION_KEY=${ENCRYPTION_KEY:-dev-encryption-key-at-least-32-chars}
      - API_ENCRYPTION_KEY=${API_ENCRYPTION_KEY:-}
      - INTERNAL_API_SECRET=${INTERNAL_API_SECRET:-dev-internal-api-secret-min-32-chars}
      - REDIS_URL=${REDIS_URL:-redis://redis:6379}
      - COPILOT_API_KEY=${COPILOT_API_KEY:-}
      - MSHIP_SYSPROMPT_OVERRIDE=${MSHIP_SYSPROMPT_OVERRIDE:-}
      - NEXT_PUBLIC_CHAT_DISABLED=${NEXT_PUBLIC_CHAT_DISABLED:-}
      - SIM_AGENT_API_URL=${SIM_AGENT_API_URL:-}
      - OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
      - SOCKET_SERVER_URL=${SOCKET_SERVER_URL:-http://realtime:3002}
      - NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL:-}
    # Lets a workflow reach a service on the Docker host. Reaching it also
    # requires naming it in EGRESS_ALLOWED_HOSTS; this only makes the name
    # resolve, which it does not on Linux by default.
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
      migrations:
        condition: service_completed_successfully
      realtime:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'curl', '-fsS', 'http://127.0.0.1:3000']
      interval: 90s
      timeout: 5s
      retries: 3
      start_period: 10s

  realtime:
    env_file:
      - path: .env
        required: false
    build:
      context: .
      dockerfile: docker/realtime.Dockerfile
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
      - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
      - BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-dev-secret-at-least-32-characters-long}
      - INTERNAL_API_SECRET=${INTERNAL_API_SECRET:-dev-internal-api-secret-min-32-chars}
      - REDIS_URL=${REDIS_URL:-redis://redis:6379}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    ports:
      - '3002:3002'
    deploy:
      resources:
        limits:
          memory: 1G
    healthcheck:
      test: ['CMD', 'curl', '-fsS', 'http://127.0.0.1:3002/health']
      interval: 90s
      timeout: 5s
      retries: 3
      start_period: 10s

  migrations:
    build:
      context: .
      dockerfile: docker/db.Dockerfile
    working_dir: /app/packages/db
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
    depends_on:
      db:
        condition: service_healthy
    command: ['bun', 'run', 'db:migrate']
    restart: 'no'

  # Backs pub/sub (live Chat task-status and table events) and the shared caches.
  # The app falls back to PostgreSQL for storage when REDIS_URL is unset, but the
  # pub/sub channels have no fallback โ€” without Redis, live status never streams.
  # Not published to the host: only the app and realtime containers need it, and
  # binding 6379 would collide with a Redis already running locally.
  redis:
    image: redis:7-alpine
    restart: always
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 5s
      retries: 5

  # Not published to the host: the other services reach it over the Compose
  # network as db:5432. A host `ports:` mapping binds every interface, and the
  # password below defaults to a well-known value. For host access use
  # `docker compose exec db psql -U postgres simstudio`.
  db:
    image: pgvector/pgvector:pg17
    restart: always
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
      - POSTGRES_DB=${POSTGRES_DB:-simstudio}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  postgres_data: