PostgreSQL

Windshift supports PostgreSQL as an alternative to SQLite for teams that need higher write concurrency or integration with existing database infrastructure.

Connection String

Pass a PostgreSQL connection string via CLI flag or environment variable:

# CLI flag
./windshift --postgres-connection-string "postgres://user:password@localhost:5432/windshift?sslmode=disable"

# Short flag
./windshift --pg-conn "postgres://user:password@localhost:5432/windshift?sslmode=disable"

# Environment variable
export POSTGRES_CONNECTION_STRING="postgres://user:password@localhost:5432/windshift?sslmode=disable"
./windshift

When a PostgreSQL connection string is provided, Windshift ignores the --db flag and uses PostgreSQL instead of SQLite.

Connection Pool

The PostgreSQL pool is bounded so a busy instance, or a burst of agent traffic, cannot exhaust the database with "too many clients already". It is sized from MAX_READ_CONNS:

Setting Default Source
Max open connections 30 MAX_READ_CONNS
Max idle connections 15 half of MAX_READ_CONNS

Idle connections are recycled on a lifetime and idle timeout. To raise the ceiling, increase MAX_READ_CONNS (--max-read-conns), but keep it well under the PostgreSQL server's own max_connections, and leave headroom for other clients and for any runner or sidecar processes.

Per-user concurrency

Independently of the pool, MAX_USER_CONCURRENCY (default 16) caps how many /api requests a single authenticated user, or agent, can have in flight at once. This keeps one heavy client from monopolising the pool. Set it to 0 to disable the cap.

The pool is managed by Go's database/sql package.

Docker Compose Setup

services:
  windshift:
    image: ghcr.io/windshiftapp/windshift:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - BASE_URL=https://windshift.example.com
      - SSO_SECRET=${SSO_SECRET}
      - POSTGRES_CONNECTION_STRING=postgres://windshift:${POSTGRES_PASSWORD}@postgres:5432/windshift?sslmode=disable
      - ATTACHMENT_PATH=/data/attachments
    volumes:
      - windshift-data:/data
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=windshift
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=windshift
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U windshift"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  windshift-data:
  postgres-data:

Schema Management

Windshift manages database schemas automatically. On startup it runs embedded migration files for all tables (items, users, tests, workflows, portal, notifications, permissions, etc.). SQL placeholders are automatically converted between SQLite (?) and PostgreSQL ($1, $2, ...) syntax.

No manual schema setup or migration commands are required.

SSL/TLS

For production PostgreSQL connections, use sslmode in the connection string:

# Require SSL
--pg-conn "postgres://user:pass@db.example.com:5432/windshift?sslmode=require"

# Verify server certificate
--pg-conn "postgres://user:pass@db.example.com:5432/windshift?sslmode=verify-full&sslrootcert=/path/to/ca.pem"

Backups

Use standard PostgreSQL backup tools:

# Logical backup
pg_dump -U windshift windshift > backup.sql

# Compressed backup
pg_dump -U windshift -Fc windshift > backup.dump

# Restore
pg_restore -U windshift -d windshift backup.dump

Migrating from SQLite

To migrate an existing SQLite database to PostgreSQL:

  1. Set up a fresh PostgreSQL database
  2. Start Windshift with the PostgreSQL connection string - it will create all tables
  3. Export data from SQLite and import into PostgreSQL using standard tooling

There is no built-in migration command at this time.