pgvector Docker: Production Setup Guide for PostgreSQL Vector Search

TL;DR — pgvector Docker: use official image, docker-compose with volumes, init.sql for auto-extension, production tuning. Markaicode: "Use pgvector/pgvector:0.7.4-pg16 with persistent volume. Setup under 5 minutes basic, 10-15 min production. Pin exact tag, never :latest. shared_buffers=2GB, effective_cache_size=6GB, maintenance_work_mem=2GB." Markaicode: "Tested 0.8.0 with PG16 on Docker 27.3.1 on EC2 G4dn. 15 min setup. random_page_cost=1.1 SSD, effective_io_concurrency=200. Resource limits prevent OOM. 1M vectors 384 dims needs 8GB." DeepWiki: "Official images multi-platform AMD64/ARM64. Single-stage build extends postgres. make OPTFLAGS=\"\" for portable compilation." Docker Hub: "docker run with POSTGRES_PASSWORD, CREATE EXTENSION vector. Standard POSTGRES_* env vars apply." Learn more with pgvector tutorial, vector search API, HNSW vs IVFFlat, and pgvector performance.

Markaicode provides the quick start: "This integration enables you to run PostgreSQL with vector similarity search inside a Docker container, ready for production AI applications. Use the official pgvector/pgvector:0.7.4-pg16 image with a persistent volume mount. Expected setup time: under 5 minutes for a basic container, plus 10-15 minutes for production hardening."

Markaicode adds production context: "We tested pgvector 0.8.0 with PostgreSQL 16 on Docker 27.3.1 running on AWS EC2 G4dn.xlarge. The full setup takes about 15 minutes, but index tuning separates production from prototypes."

pgvector Docker Architecture

flowchart TD subgraph Docker["Docker Container"] Image["pgvector/pgvector:0.8.0-pg16
official image, pinned tag"] PG["PostgreSQL 16
+ vector extension"] Init["init.sql
CREATE EXTENSION vector
auto-runs on first init"] Conf["postgresql.conf
shared_buffers, maintenance_work_mem
random_page_cost=1.1"] end subgraph Volumes["Persistent Storage"] Data["Named Volume
pgvector_data
/var/lib/postgresql/data"] Backups["Backup Volume
pgvector_backups
pg_dump, WAL archives"] end subgraph Compose["docker-compose.yml"] Service["pgvector service
restart: unless-stopped
ports: 5432"] Resources["Resource Limits
memory: 4-8GB
cpus: 2-4"] Ulimits["ulimits
nofile: 65536"] end subgraph Network["Network"] Port["Port 5432
exposed to host"] Pool["PgBouncer
connection pooling
max_client_conn=400"] end subgraph App["Application"] FastAPI["FastAPI Service
asyncpg pool"] Python["Python App
psycopg2 / asyncpg"] end Image --> PG Init --> PG Conf --> PG Data --> PG Backups --> PG Service --> Image Resources --> Service Ulimits --> Service PG --> Port Port --> Pool Pool --> FastAPI Pool --> Python

Docker Image Comparison

Image Tag PostgreSQL pgvector Platform Best For
pgvector/pgvector 0.8.0-pg16 16 0.8.0 AMD64, ARM64 Production default
pgvector/pgvector 0.8.0-pg17 17 0.8.0 AMD64, ARM64 Latest PG
pgvector/pgvector pg16 16 latest AMD64, ARM64 Rolling latest
dhi.io/pgvector 0.8.2-pg18 18 0.8.2 AMD64, ARM64 Hardened image
postgres:16 + apt 16 via apt AMD64, ARM64 Custom base

Production Configuration

Parameter Value Purpose
shared_buffers 2GB (25% RAM) PostgreSQL shared memory
effective_cache_size 6GB (75% RAM) Query planner disk cache estimate
maintenance_work_mem 2GB HNSW index build memory
random_page_cost 1.1 SSD-optimized query planner
effective_io_concurrency 200 SSD concurrent I/O
max_connections 100 Connection limit
memory limit 4-8GB Container resource limit
cpus 2-4 Container CPU limit
nofile 65536 File descriptor limit
restart unless-stopped Auto-restart policy

Implementation

from dataclasses import dataclass
from typing import Optional

@dataclass
class PgvectorDockerSetup:
    """pgvector Docker production setup guide."""

    def get_docker_run(self) -> str:
        """Single container docker run command."""
        return (
            "# Pull the image (pin exact tag)\n"
            "docker pull pgvector/pgvector:0.8.0-pg16\n"
            "\n"
            "# Run with persistent storage\n"
            "docker run -d \\\n"
            "  --name pgvector-prod \\\n"
            "  -e POSTGRES_PASSWORD=your_secure_password \\\n"
            "  -e POSTGRES_DB=vectordb \\\n"
            "  -p 5432:5432 \\\n"
            "  -v pgvector_data:/var/lib/postgresql/data \\\n"
            "  -v ./init.sql:/docker-entrypoint-initdb.d/init.sql \\\n"
            "  --memory=8g --cpus=4 \\\n"
            "  --restart unless-stopped \\\n"
            "  pgvector/pgvector:0.8.0-pg16"
        )

    def get_docker_compose(self) -> str:
        """Production docker-compose.yml."""
        return (
            "version: '3.9'\n"
            "\n"
            "services:\n"
            "  pgvector:\n"
            "    image: pgvector/pgvector:0.8.0-pg16\n"
            "    container_name: pgvector-prod\n"
            "    restart: unless-stopped\n"
            "    ports:\n"
            "      - '5432:5432'\n"
            "    environment:\n"
            "      POSTGRES_DB: vectordb\n"
            "      POSTGRES_USER: vector_user\n"
            "      POSTGRES_PASSWORD: ${PG_PASSWORD}\n"
            "    volumes:\n"
            "      - pgvector_data:/var/lib/postgresql/data\n"
            "      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro\n"
            "      - ./postgresql.conf:/etc/postgresql/postgresql.conf:ro\n"
            "    command: >\n"
            "      postgres -c config_file=/etc/postgresql/postgresql.conf\n"
            "    deploy:\n"
            "      resources:\n"
            "        limits:\n"
            "          memory: 8g\n"
            "          cpus: '4'\n"
            "        reservations:\n"
            "          memory: 4g\n"
            "    ulimits:\n"
            "      nofile:\n"
            "        soft: 65536\n"
            "        hard: 65536\n"
            "    healthcheck:\n"
            "      test: ['CMD-SHELL',\n"
            "        'pg_isready -U vector_user -d vectordb']\n"
            "      interval: 10s\n"
            "      timeout: 5s\n"
            "      retries: 5\n"
            "\n"
            "volumes:\n"
            "  pgvector_data:"
        )

    def get_init_sql(self) -> str:
        """init.sql for auto-extension creation."""
        return (
            "-- init.sql: runs on first database initialization\n"
            "CREATE EXTENSION IF NOT EXISTS vector;\n"
            "\n"
            "-- Optional: create schema for vector tables\n"
            "CREATE SCHEMA IF NOT EXISTS vectors;\n"
            "\n"
            "-- Optional: set default schema\n"
            "SET search_path TO vectors, public;\n"
            "\n"
            "-- Note: This script only runs on first init.\n"
            "-- For existing databases, run manually:\n"
            "-- docker exec -it pgvector-prod \\\n"
            "--   psql -U vector_user -d vectordb \\\n"
            "--   -c 'CREATE EXTENSION IF NOT EXISTS vector;'"
        )

    def get_postgresql_conf(self) -> str:
        """Production postgresql.conf for vector workloads."""
        return (
            "# postgresql.conf for pgvector production\n"
            "\n"
            "# Memory (for 8GB host)\n"
            "shared_buffers = 2GB\n"
            "effective_cache_size = 6GB\n"
            "maintenance_work_mem = 2GB\n"
            "work_mem = 64MB\n"
            "\n"
            "# SSD optimization\n"
            "random_page_cost = 1.1\n"
            "effective_io_concurrency = 200\n"
            "\n"
            "# Connections\n"
            "max_connections = 100\n"
            "\n"
            "# WAL\n"
            "wal_buffers = 16MB\n"
            "min_wal_size = 1GB\n"
            "max_wal_size = 4GB\n"
            "\n"
            "# Logging\n"
            "log_min_duration_statement = 1000\n"
            "log_connections = on\n"
            "log_disconnections = on\n"
            "\n"
            "# Autovacuum\n"
            "autovacuum = on\n"
            "autovacuum_naptime = 30s\n"
            "\n"
            "# Parallel query\n"
            "max_parallel_workers_per_gather = 2\n"
            "max_parallel_workers = 4"
        )

    def get_custom_dockerfile(self) -> str:
        """Custom Dockerfile building from postgres:16."""
        return (
            "# Option 1: apt install (Debian Bookworm)\n"
            "FROM postgres:16\n"
            "\n"
            "RUN apt-get update \\\n"
            "    && apt-get install -y \\\n"
            "       postgresql-16-pgvector \\\n"
            "    && rm -rf /var/lib/apt/lists/*\n"
            "\n"
            "# Option 2: compile from source\n"
            "FROM postgres:16\n"
            "\n"
            "RUN apt-get update \\\n"
            "    && apt-get install -y \\\n"
            "       build-essential \\\n"
            "       postgresql-server-dev-16 \\\n"
            "       git \\\n"
            "    && git clone --branch v0.8.0 \\\n"
            "       https://github.com/pgvector/pgvector.git \\\n"
            "       /tmp/pgvector \\\n"
            "    && cd /tmp/pgvector \\\n"
            "    && make clean \\\n"
            "    && make OPTFLAGS=\"\" \\\n"
            "    && make install \\\n"
            "    && rm -rf /tmp/pgvector \\\n"
            "    && apt-get remove -y build-essential git \\\n"
            "    && apt-get autoremove -y \\\n"
            "    && rm -rf /var/lib/apt/lists/*"
        )

    def get_verify_commands(self) -> str:
        """Commands to verify pgvector Docker setup."""
        return (
            "# Verify container is running\n"
            "docker ps | grep pgvector\n"
            "\n"
            "# Verify PostgreSQL version\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c 'SELECT version();'\n"
            "\n"
            "# Verify pgvector extension\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c \"SELECT extname, extversion\n"
            "        FROM pg_extension\n"
            "        WHERE extname = 'vector';\"\n"
            "\n"
            "# Test vector type\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c \"CREATE TABLE test (id serial PRIMARY KEY,\n"
            "        embedding vector(3));\"\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c \"INSERT INTO test (embedding)\n"
            "        VALUES ('[1,2,3]'), ('[4,5,6]');\"\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c \"SELECT * FROM test\n"
            "        ORDER BY embedding <=> '[3,1,2]'\n"
            "        LIMIT 1;\"\n"
            "\n"
            "# Check memory inside container\n"
            "docker exec pgvector-prod free -h\n"
            "\n"
            "# Check PostgreSQL config\n"
            "docker exec pgvector-prod \\\n"
            "  psql -U vector_user -d vectordb \\\n"
            "  -c 'SHOW shared_buffers;'\n"
            "  -c 'SHOW maintenance_work_mem;'"
        )

    def get_production_tips(self) -> dict:
        """Production deployment tips."""
        return {
            "image_tagging": (
                "Pin exact tag (0.8.0-pg16), never :latest. "
                "Ensures reproducible builds."
            ),
            "resource_limits": (
                "Set memory limits to prevent OOM during index builds. "
                "1M vectors with 384 dims needs 8GB. "
                "HNSW index builds consume gigabytes of RAM."
            ),
            "data_persistence": (
                "Use named volumes, not bind mounts. "
                "Named volumes are managed by Docker and "
                "survive container recreation."
            ),
            "init_script": (
                "init.sql only runs on first database initialization. "
                "For existing databases, run CREATE EXTENSION manually."
            ),
            "config_file": (
                "Mount custom postgresql.conf and use "
                "command: postgres -c config_file=... "
                "to override default config."
            ),
            "healthcheck": (
                "Add healthcheck with pg_isready. "
                "Docker restarts unhealthy containers."
            ),
            "ssd_tuning": (
                "random_page_cost=1.1, effective_io_concurrency=200 "
                "for SSD storage. Default values assume spinning disks."
            ),
            "explain_analyze": (
                "Always run EXPLAIN ANALYZE on vector queries "
                "to catch sequential scans in production."
            ),
            "multi_platform": (
                "Official images support AMD64 and ARM64. "
                "OPTFLAGS='' ensures portable compilation "
                "across CPU architectures."
            ),
        }

pgvector Docker Checklist

  • [ ] Use official pgvector/pgvector Docker image — pin exact tag, never :latest
  • [ ] Current recommended: pgvector/pgvector:0.8.0-pg16 (PostgreSQL 16, pgvector 0.8.0)
  • [ ] Images are multi-platform: AMD64 and ARM64
  • [ ] OPTFLAGS="" ensures portable compilation across CPU architectures
  • [ ] docker run -d --name pgvector pgvector/pgvector:0.8.0-pg16 for quick start
  • [ ] Setup time: under 5 minutes basic, 10-15 minutes production hardening
  • [ ] Docker overhead: 3-5% memory vs bare-metal PostgreSQL
  • [ ] Operational wins (deploy, scale, replicate) far outweigh overhead
  • [ ] Use docker-compose for production: pin image, set resources, mount config
  • [ ] Named volumes for data persistence: pgvector_data:/var/lib/postgresql/data
  • [ ] Mount init.sql to /docker-entrypoint-initdb.d/init.sql for auto-extension
  • [ ] init.sql: CREATE EXTENSION IF NOT EXISTS vector
  • [ ] init.sql only runs on first database initialization, not existing databases
  • [ ] For existing databases: docker exec -it pgvector psql -U user -c "CREATE EXTENSION vector"
  • [ ] Mount custom postgresql.conf: command: postgres -c config_file=/etc/postgresql/postgresql.conf
  • [ ] shared_buffers = 2GB (25% of RAM for 8GB host)
  • [ ] effective_cache_size = 6GB (75% of RAM for 8GB host)
  • [ ] maintenance_work_mem = 2GB for HNSW index builds
  • [ ] random_page_cost = 1.1 for SSD-optimized query planner
  • [ ] effective_io_concurrency = 200 for SSD concurrent I/O
  • [ ] max_connections = 100 for most workloads
  • [ ] work_mem = 64MB for sorting and hashing
  • [ ] Set resource limits: memory 4-8GB, cpus 2-4
  • [ ] Set reservations: memory 2-4GB
  • [ ] Set ulimits: nofile soft 65536, hard 65536
  • [ ] restart: unless-stopped for auto-restart
  • [ ] Add healthcheck: pg_isready -U user -d db
  • [ ] Tested on AWS EC2 G4dn.xlarge with Docker 27.3.1, Ubuntu 24.04
  • [ ] 1M vectors with 384 dims needs at least 8GB RAM
  • [ ] HNSW index builds consume gigabytes of RAM — set memory limits
  • [ ] Always run EXPLAIN ANALYZE on vector queries to catch seq scans
  • [ ] Custom Dockerfile: FROM postgres:16, RUN apt-get install postgresql-16-pgvector
  • [ ] Or compile from source: make OPTFLAGS="" for portable compilation
  • [ ] pgvector available in Debian Bookworm as postgresql-16-pgvector
  • [ ] Standard POSTGRES_* environment variables apply (POSTGRES_DB, USER, PASSWORD)
  • [ ] Verify setup: SELECT extname, extversion FROM pg_extension WHERE extname = 'vector'
  • [ ] Test vector type: CREATE TABLE test (embedding vector(3))
  • [ ] Test search: SELECT * FROM test ORDER BY embedding <=> '[1,2,3]' LIMIT 1
  • [ ] Check memory inside container: docker exec pgvector free -h
  • [ ] Check config: SHOW shared_buffers, SHOW maintenance_work_mem
  • [ ] Use PgBouncer for connection pooling in front of pgvector
  • [ ] Backup: docker exec pgvector pg_dump > backup.sql
  • [ ] pgvector 0.8.2 (February 2026): HNSW, IVFFlat, halfvec, sparsevec, binary quantization
  • [ ] Docker Hardened Images available: dhi.io/pgvector with security hardening
  • [ ] Read pgvector tutorial for SQL setup
  • [ ] Read vector search API for FastAPI
  • [ ] Read HNSW vs IVFFlat for index selection
  • [ ] Read pgvector performance for tuning
  • [ ] Read semantic search for search
  • [ ] Read vector databases for fundamentals
  • [ ] Test: container starts and PostgreSQL is accessible on port 5432
  • [ ] Test: vector extension is created automatically via init.sql
  • [ ] Test: vector type and distance operators work correctly
  • [ ] Test: HNSW index builds within memory limits
  • [ ] Test: healthcheck detects container health correctly
  • [ ] Test: data persists across container restarts
  • [ ] Document image tag, compose config, init.sql, postgresql.conf, and resource limits

FAQ

How do you run pgvector in Docker?

Use the official pgvector/pgvector Docker image with a persistent volume and init.sql script. Markaicode: "Use the official pgvector/pgvector:0.7.4-pg16 image with a persistent volume mount. Expected setup time: under 5 minutes for a basic container, plus 10-15 minutes for production hardening. Run pgvector in Docker in one command — docker run -d --name pgvector pgvector/pgvector:0.7.4-pg16. Pin the exact tag, never :latest." Docker Hub: "docker run --name some-pgvector -e POSTGRES_PASSWORD=mysecretpassword -d dhi.io/pgvector:. After container is running, load extension: docker exec -it some-pgvector psql -U postgres -c CREATE EXTENSION vector." Steps: (1) docker pull pgvector/pgvector:0.8.0-pg16. (2) docker run with -e POSTGRES_PASSWORD, -v for data, -p 5432. (3) Mount init.sql to /docker-entrypoint-initdb.d/ for auto-extension. (4) Connect with psql or Python.

How do you set up pgvector with docker-compose for production?

Create a docker-compose.yml with the pgvector image, named volumes, init.sql, resource limits, and PostgreSQL tuning. Markaicode: "docker-compose.yml with pgvector/pgvector:0.7.4-pg16, POSTGRES_SHARED_BUFFERS=2GB, POSTGRES_EFFECTIVE_CACHE_SIZE=6GB, POSTGRES_MAINTENANCE_WORK_MEM=2GB, named volume for data, init.sql for extension, deploy resources limits memory 8GB cpus 4, ulimits nofile 65536." Markaicode: "Tested pgvector 0.8.0 with PostgreSQL 16 on Docker 27.3.1 on AWS EC2 G4dn.xlarge. Full setup takes about 15 minutes. Use docker-compose that pins image, sets resources, mounts custom config. random_page_cost=1.1 for SSD, effective_io_concurrency=200." Production compose: image pinned, restart unless-stopped, named volume, custom postgresql.conf, resource limits (memory 4g, cpus 2), reservations (memory 2g).

How do you auto-enable the pgvector extension in Docker?

Mount an init.sql script to /docker-entrypoint-initdb.d/ that runs CREATE EXTENSION vector automatically on first startup. Markaicode: "init.sql: CREATE EXTENSION IF NOT EXISTS vector. Mount to /docker-entrypoint-initdb.d/init.sql. The init script runs automatically on first database initialization." VibeCode: "Init script automatically creates vector extension. Works out of the box with no manual intervention. For existing databases, manual extension creation required: docker-compose exec postgres psql -U user -d db -c CREATE EXTENSION IF NOT EXISTS vector." Docker Hub: "docker exec -it some-pgvector psql -U postgres -c CREATE EXTENSION vector. The image inherits upstream PostgreSQL entrypoint, so standard POSTGRES_* environment variables apply." Note: init.sql only runs on first initialization, not on existing databases.

What PostgreSQL configuration should you tune for pgvector in Docker?

Tune shared_buffers, effective_cache_size, maintenance_work_mem, random_page_cost, and effective_io_concurrency for vector workloads. Markaicode: "POSTGRES_SHARED_BUFFERS=2GB, POSTGRES_EFFECTIVE_CACHE_SIZE=6GB, POSTGRES_MAINTENANCE_WORK_MEM=2GB for vector workloads." Markaicode: "random_page_cost=1.1 for SSD-optimized, effective_io_concurrency=200. Resource limits prevent OOM during index builds. For 1M vectors with 384 dims, need at least 8GB. Always run EXPLAIN ANALYZE on vector queries to catch seq scans." Configuration: (1) shared_buffers: 25% of RAM (2GB for 8GB host). (2) effective_cache_size: 75% of RAM (6GB for 8GB host). (3) maintenance_work_mem: 2GB for HNSW index builds. (4) random_page_cost: 1.1 for SSD. (5) effective_io_concurrency: 200 for SSD. (6) max_connections: 100 for most workloads. Mount custom postgresql.conf and use command: postgres -c config_file=/etc/postgresql/postgresql.conf.

How do you build a custom pgvector Docker image?

Build from postgres:16 base image and install pgvector via apt, or use the official pgvector Dockerfile as a base. DeepWiki: "pgvector provides official Docker images based on upstream postgres. Images are multi-platform (AMD64 and ARM64). Single-stage build extends official PostgreSQL image. Install build-essential and postgresql-server-dev, make OPTFLAGS=\"\" for portable compilation, make install, clean up. OPTFLAGS=\"\" disables architecture-specific optimizations to avoid Illegal instruction errors on different processors." Markaicode: "If you cannot use pre-built image, build from postgres:16: RUN apt-get update && apt-get install -y postgresql-16-pgvector (available in Debian Bookworm). Rebuild image. Slower but works when you need custom base." Custom Dockerfile: FROM postgres:16, RUN apt-get update && apt-get install -y postgresql-16-pgvector, or compile from source with make OPTFLAGS="" for portability.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →