Skip to content

Docker 部署 PostgreSQL:数据库服务、pgvector 向量检索 ​

PostgreSQL 是社区维护的开源关系数据库,严格遵循 ACID 事务,除标准 SQL 外还原生支持 JSON、数组等复合类型,靠多版本并发控制(MVCC)支撑高并发读写;更重要的是扩展性——通过插件机制可以给数据库加装新能力,地理检索用 PostGIS,向量检索用 pgvector。pgvector 为 PostgreSQL 增加 vector 向量类型、距离运算符和近似最近邻(ANN)索引,让"业务数据 + 语义检索"留在同一个库、同一个事务里,不必再引入独立向量数据库和同步链路。

部署结论 ​

项目本文采用的值
项目地址PostgreSQL · pgvector
官方镜像pgvector/pgvector:0.8.6-pg18-bookworm
项目版本PostgreSQL 18.6(pgvector 0.8.6)
主部署方法Docker Compose + 版本化 pgvector/pgvector 预装镜像
备选方法同一版本镜像的 docker run 临时体验
对外端口127.0.0.1:5432(仅本机回环)
持久化位置命名卷 pgdata → 容器内 /var/lib/postgresql
依赖服务无(单容器)
部署日期2026-09-23

镜像与版本选择 ​

pgvector/pgvector 镜像叠加在 PostgreSQL 官方镜像之上,本文固定使用 0.8.6-pg18-bookworm,避免 latest 或未声明大版本的 tag 漂移。

选 tag 时看三件事:

  • 数据库版本:18.6 是本文验证的小版本,18 跟随 18.x 最新小版本滚动,latest 会跨大版本漂移。长期部署禁止使用 latest,小版本升级是显式动作而不是意外。
  • 基础系统:bookworm 基于 Debian 12。Debian 系变体适合直接使用预装扩展镜像;如果切换到 Alpine 或自行构建镜像,需要重新验证扩展包和数据目录。
  • 架构:同一 tag 覆盖 amd64/arm64,拉取后以 docker image inspect 的 RepoDigests 比对官方公示摘要。

pgvector 与 PostgreSQL 大版本已经同时编码在镜像 tag 中,升级时必须同时检查两者兼容性。

架构说明 ​

pgvector 不是独立服务,而是加载进 PostgreSQL 服务端的扩展:扩展二进制已经安装在镜像中,对每个数据库执行一次 CREATE EXTENSION vector,该库即获得向量类型、距离运算符和 hnsw、ivfflat 两种索引访问方法。应用通过一条连接同时写业务行和向量列。

镜像由 PostgreSQL 官方基础层和 pgvector 扩展层组成。Compose 直接使用版本化预装镜像,避免读者在部署过程中临时构建扩展镜像。

Mermaid 流程图
查看源码
flowchart LR
  A[应用 / psql 客户端] -->|5432| B[容器: pgvector/pgvector 0.8.6-pg18-bookworm]
  I[./init/*.sql<br/>首次初始化脚本] --> B
  B --> C[(命名卷 pgdata<br/>/var/lib/postgresql)]

前置条件 ​

  • Docker Engine 与 Compose 插件可用:docker compose version 有版本输出才能继续。
  • 宿主机 5432 端口空闲:lsof -i :5432 无输出;被占用则把端口映射第一段的宿主机端口改为其他值。
  • 磁盘余量:命名卷初始为几十 MB 量级,之后随表数据和索引增长。

国内网络直连镜像仓库失败时,优先配置 registry mirror 后再按本文步骤执行,Compose 中的官方镜像引用不需要改动;通过第三方加速拉取后,用下面的命令取回镜像摘要并与仓库页公示的 digest 比对,镜像源配置方法见Docker 镜像源配置教程。

bash
docker image inspect <镜像> --format '{{json .RepoDigests}}'

docker run 快速启动 ​

只为感受产物,不作为长期形态。

bash
QUICK_PASSWORD="$(openssl rand -hex 16)"
docker run --rm -d --name pg-quick \
  --env POSTGRES_PASSWORD="$QUICK_PASSWORD" \
  -p 127.0.0.1:5432:5432 \
  pgvector/pgvector:0.8.6-pg18-bookworm
  • --name 自定义容器名;
  • -d 后台运行;
  • -p 端口映射;

快速部署成功后验证(临时实例使用初始化超级用户):

bash
# 进入容器
docker exec -it pg-quick psql -U postgres

# 查询数据库版本信息
docker exec pg-quick psql -U postgres -tAc "SELECT version()"
# PostgreSQL 18.6 (Debian 18.6-1.pgdg12+2) on aarch64-unknown-linux-gnu ...

注意本命令没有挂数据卷:数据写在容器运行时的匿名卷里,容器删除后不保留业务数据。体验完执行 docker stop pg-quick,随后进入正式部署。

Docker Compose 长期运行 ​

长期运行需要固定的口令注入方式、初始化脚本挂载、健康检查和命名卷的版本管理,一个 compose.yaml 承载比记一串 docker run 参数可靠。

工作目录 ​

text
postgres/
├── compose.yaml
├── .env
├── init/
│   ├── 01-schema.sql
│   └── 02-vector.sql
└── backups/

创建 .env 配置文件 ​

在目录下创建 .env 文件。口令通过随机值生成,不把可复用的密码写进文章:

env
# .env 文件(需设置权限 chmod 600,仅所有者可读)
POSTGRES_USER=postgres
POSTGRES_DB=appdb
  • .env 只保存本地配置和生成的口令,权限设为 600。

或者可以使用命令一键初始化 .env 文件:

bash
cat > .env <<'EOF'
POSTGRES_USER=postgres
POSTGRES_DB=appdb
EOF
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .env

该命令会创建或覆盖当前目录下的 .env 文件;运行后不要把 .env 提交到版本库。

编写 compose.yaml 文件 ​

yaml
services:
  postgres:
    image: pgvector/pgvector:0.8.6-pg18-bookworm
    restart: unless-stopped
    container_name: pg-service
    shm_size: 256mb
    ports:
      - "127.0.0.1:5432:5432"  # 仅本地监听,通过应用或代理访问
    environment:
      POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env}
      TZ: Asia/Shanghai
    volumes:
      - pgdata:/var/lib/postgresql
      - ./init:/docker-entrypoint-initdb.d:ro  # 初始化脚本目录(可选)
    deploy:
      resources:
        limits:
          cpus: '2'  # 限制 CPU 使用率
          memory: 4G  # 限制最大内存
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  pgdata:
  • restart: unless-stopped 让宿主机重启后数据库随 Docker 自启,手动 stop 则不复活;
  • ${POSTGRES_PASSWORD:?...} 缺值时启动直接失败而不是空口令裸奔;
  • volumes 分别解决"数据活过容器"和"新库自动建表";
  • healthcheck 让 docker compose ps 的 healthy 成为可脚本化的成功信号。

挂载路径有大版本陷阱:PostgreSQL 18 起数据目录变为 /var/lib/postgresql/18/docker,镜像声明卷随之改为 /var/lib/postgresql,上面的写法适用于 18 及以上,且保留跨大版本 pg_upgrade --link 快速路径的前提。

警告:若改用 17 或更低版本镜像,数据卷必须挂载到 /var/lib/postgresql/data;挂 /var/lib/postgresql 不会持久化——运行时会自动创建匿名卷接管数据,容器删除重建后数据随之丢失。

也可以不用命名卷而挂宿主机目录(bind mount):./binddata:/var/lib/postgresql。重建容器后数据应继续保留,宿主机目录里会出现 18/ 子目录。两种挂载的取舍:命名卷由 Docker 管理权限、跨主机一致,适合默认选择;bind mount 路径直观、方便 rsync 式冷备份,但在 Linux 上宿主机目录属主要与容器内 postgres 用户(UID 999)匹配,否则 initdb 报权限错误。

启动前先静态校验:

bash
docker compose config --quiet && echo "config OK"

.env 缺口令时该命令直接报错退出。校验通过后:

bash
docker compose up -d

初始化 01-schema.sql ​

init/01-schema.sql 演示首次初始化(建一张纯业务表):

sql
CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  title text NOT NULL
);
INSERT INTO documents (title) VALUES ('baseline-business-row');

init/02-vector.sql 在首次创建的 appdb 中启用扩展:

sql
CREATE EXTENSION IF NOT EXISTS vector;

验证 PostgreSQL 服务 ​

下列命令用于分层验收:先检查容器状态,再检查 PostgreSQL 版本、初始化脚本和 pgvector 扩展。

服务状态——健康检查从 starting 变为 healthy 表示可接受连接:

bash
docker compose ps
# NAME                      IMAGE                                      STATUS
# pg-service                pgvector/pgvector:0.8.6-pg18-bookworm      Up ... (healthy)

数据库接口——服务端版本与初始化脚本结果:

bash
docker compose exec postgres psql -U postgres -d appdb -tAc "SELECT version()"
# PostgreSQL 18.6 (Debian 18.6-1.pgdg12+2) on aarch64-unknown-linux-gnu ...

docker compose exec postgres psql -U postgres -d appdb -tAc "SELECT count(*) FROM documents"
# 1

docker compose logs postgres 2>&1 | grep 'docker-entrypoint-initdb.d'
# /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/01-schema.sql

扩展验收——预装镜像已经包含 pgvector,初始化脚本会在 appdb 中启用扩展:

bash
docker compose exec postgres psql -U postgres -d appdb -tAc "SELECT extversion FROM pg_extension WHERE extname = 'vector'"
# 0.8.6

启用并验证 pgvector ​

每个数据库都需要单独启用扩展。首次创建 appdb 时,init/02-vector.sql 自动执行;已有数据卷切换到本文镜像后,手动执行以下命令:

bash
docker compose exec -T postgres psql -U postgres -d appdb \
  -c 'CREATE EXTENSION IF NOT EXISTS vector;' \
  -c "SELECT extversion FROM pg_extension WHERE extname = 'vector';"

不要删除现有 pgdata;只替换镜像并重建容器即可。大版本不一致时不能复用数据卷,必须按“升级与回滚”章节执行迁移。

连接数据库与基本使用 ​

容器内命令行(免密,走 trust 的本地套接字):

bash
# 查看当前数据库信息
docker compose exec -T postgres psql -U postgres -d template1 -c '\l' -c '\du'

# 链接数据库
docker compose exec postgres psql -U postgres -d appdb

常用元命令:

text
\l      -- 列出数据库:appdb / postgres / template0 / template1
\dt     -- 列出当前库的表:public.documents(owner: postgres)
\du     -- 列出角色:postgres(Superuser, Create role, Create DB, ...)
\d 表名  -- 查看表结构;\conninfo 查看当前连接
\c database_name  -- 切换到另一个数据库
CREATE DATABASE database_name;   -- 创建一个新的数据库

退出用 \q。日常 SQL 与其他 PostgreSQL 无异,下面演示容器场景最常踩坑的三件事:专用应用账号、最小授权、连接串。

sql
-- 为业务连接创建专用账号;口令自行生成,不要用示例值
CREATE ROLE appuser LOGIN PASSWORD '应用口令';
GRANT CONNECT ON DATABASE appdb TO appuser;
GRANT USAGE, CREATE ON SCHEMA public TO appuser;

GRANT ... ON SCHEMA public 在新版本 PostgreSQL 里是必要动作:15 起普通用户对 public 模式默认没有建表权限。表级读写授权按需执行:

sql
GRANT SELECT, INSERT, UPDATE, DELETE ON documents TO appuser;
GRANT USAGE ON SEQUENCE documents_id_seq TO appuser;

应用侧连接串(宿主机端口映射为回环时从本机连):

text
postgresql://appuser:应用口令@127.0.0.1:5432/appdb

验收权限边界时,以 appuser 经 TCP 连接执行 SELECT current_user, current_database() 应返回 appuser|appdb;执行 DROP TABLE documents 应返回 ERROR: must be owner of table documents,确认应用账号不能修改表结构。

远程访问与时区配置 ​

常见误解是"远程访问要改 postgresql.conf 和 pg_hba.conf"。官方镜像的出厂配置已经替你做好了:SHOW listen_addresses 应返回 *(容器内监听所有网卡),pg_hba.conf 默认规则为——

text
local   all   all                         trust
host    all   all   127.0.0.1/32          trust
host    all   all   ::1/128               trust
host    all   all   all                   scram-sha-256

容器内本地连接免密(trust 只作用于套接字与回环),其余地址进来一律走 scram-sha-256 口令认证。所以从局域网其他机器连入,不需要进容器改任何配置文件,要控制的只是端口暴露面:

  • 只给本机上的应用:保持 127.0.0.1:5432:5432。
  • 给同网段某台应用服务器:把映射改为宿主机内网 IP 绑定(如 192.168.1.10:5432:5432),配合宿主机防火墙白名单,仍不要裸绑 0.0.0.0 暴露公网。
  • 跨网段:优先 SSH 隧道或 VPN,而不是放宽数据库认证。

时区:容器默认 SHOW timezone 返回 Etc/UTC。要改成东八区,在 Compose 服务下加 command: 覆写服务端参数(改环境变量 TZ 不影响已 initdb 的数据目录默认值):

yaml
    command: ["postgres", "-c", "timezone=Asia/Shanghai"]

docker compose up -d --force-recreate 后检查 SHOW timezone 应返回 Asia/Shanghai。这只影响服务端显示时区;跨时区存储建议统一 timestamptz 类型,由客户端决定展示。

首次初始化:业务表与向量索引 ​

以文档语义检索为例,假设嵌入模型输出 768 维向量:

sql
-- 创建一个存储文档块和对应向量的表
CREATE TABLE document_chunks (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL, -- 文档文本内容
    embedding vector(1536), -- 向量,维度是1536
    metadata JSONB, -- 可以存一些元信息,比如来源文件、页码等
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入示例数据。这里的向量值是随便写的,仅作演示。
INSERT INTO document_chunks (content, embedding) VALUES
('PostgreSQL是一个功能强大的开源关系数据库。', '[0.1, 0.2, 0.3, ...]'), -- 这里省略了1536个数字
('pgvector扩展为PostgreSQL增加了向量相似性搜索能力。', '[0.4, 0.5, 0.6, ...]'),
('RAG应用利用向量数据库检索相关信息来增强大模型生成。', '[0.7, 0.8, 0.9, ...]');


CREATE TABLE articles (
  id bigserial PRIMARY KEY,
  title text NOT NULL,
  embedding vector(768) NOT NULL
);

CREATE INDEX ON articles USING hnsw (embedding vector_cosine_ops);

查询用余弦距离运算符 <=>,$1 为查询向量参数:

sql
SELECT id, title, 1 - (embedding <=> $1) AS similarity
FROM articles
ORDER BY embedding <=> $1
LIMIT 10;

POSTGRES_PASSWORD、POSTGRES_DB 这类镜像环境变量只在数据目录为空的首次初始化时生效。卷已存在时改 .env 重启不会改口令,改口令执行 SQL:

sql
ALTER USER postgres WITH PASSWORD '新口令';
ALTER USER appuser WITH PASSWORD '新口令';

pgvector 用法与调优 ​

先用一个 4 维玩具例子看查询形状。建表插入三条文档向量(实际维度由嵌入模型决定,768/1536 都常见):

sql
CREATE TABLE demo_articles (id bigserial PRIMARY KEY, title text, embedding vector(4) NOT NULL);
INSERT INTO demo_articles (title, embedding) VALUES
  ('PostgreSQL 备份手册', '[0.9,0.1,0.1,0.0]'),
  ('向量索引调优笔记', '[0.85,0.2,0.15,0.05]'),
  ('番茄炒蛋做法', '[0.05,0.9,0.1,0.2]');

SELECT id, title, round((1 - (embedding <=> '[0.9,0.1,0.1,0.0]'))::numeric, 4) AS similarity
FROM demo_articles
ORDER BY embedding <=> '[0.9,0.1,0.1,0.0]'
LIMIT 3;
--  id |        title        | similarity
-- ----+---------------------+------------
--   1 | PostgreSQL 备份手册 |     1.0000
--   2 | 向量索引调优笔记    |     0.9895
--   3 | 番茄炒蛋做法        |     0.1714

1 - 距离 把余弦距离换算成"越大越相似",配 WHERE 1 - (embedding <=> $1) > 0.5 即可过滤低相关命中。下面是生产规模的硬约束与参数。

存储上限:vector 最多 16,000 维,halfvec(半精度)同样 16,000 维;单条 vector 占用 4 × 维度 + 8 字节,容量规划按此估算。超过 2,000 维无法用默认方式建索引,可改存 halfvec(索引上限 4,000 维)、用 binary quantization(64,000 维),或做降维。

距离运算符与索引操作符类必须配对:<-> L2、<#> 内积(返回负值,因为 Postgres 索引只支持升序扫描)、<=> 余弦、<+> L1。vector_l2_ops 建的索引只加速 L2 查询,规划器不会用 L2 索引跑余弦查询,每个要用的距离函数都要单独建索引。

HNSW 与 IVFFlat 的取舍:HNSW 查询更好、构建更慢更占内存,空表即可建;IVFFlat 构建快省内存,用召回换速度,且必须表里有数据后再建(需要聚类训练)。

sql
-- HNSW:默认 m = 16、ef_construction = 64
CREATE INDEX ON articles USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- IVFFlat:lists 起步取 行数/1000(最多约 100 万行),更大表取 sqrt(行数)
CREATE INDEX ON articles USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

查询期精度参数:HNSW 默认 hnsw.ef_search = 40,调大换召回;IVFFlat 默认 ivfflat.probes = 1,经验起步值 sqrt(lists):

sql
SET hnsw.ef_search = 100;
SET ivfflat.probes = 10;

近似索引的过滤发生在索引扫描之后:带 WHERE 时命中行数可能少于 LIMIT。0.8.0 起可开启迭代索引扫描,自动扩大扫描直到凑够结果:

sql
SET hnsw.iterative_scan = strict_order;

安装与使用其他 PostgreSQL 扩展 ​

三步部署的镜像分层同样服务其他扩展,按来源分四条路径。

其一,contrib 内置扩展直接用。官方 postgres 镜像已带 PostgreSQL contrib 套件,pg_available_extensions 可查清单,CREATE EXTENSION 即可安装。需要写共享内存的扩展要额外预加载:未配置预加载时查询会报 pg_stat_statements must be loaded via "shared_preload_libraries";在 Compose 服务下加一行后重建并重新检查:

yaml
    command: ["postgres", "-c", "shared_preload_libraries=pg_stat_statements"]

其二,PGDG apt 包可以按自定义 Dockerfile 的方式装进派生镜像。包名格式 postgresql-<主版本>-<扩展名>,PostGIS 即 postgresql-18-postgis-3;只有在预装镜像不能满足需求时才采用这条路径,并重新验证构建结果。

其三,使用版本化的预装集成镜像。本文的 pgvector/pgvector 即此类:直接使用匹配 PostgreSQL 大版本的 tag,每个数据库执行一次 CREATE EXTENSION vector;。

其四,在存量部署内编译安装扩展:需要完整编译工具链,且每次镜像重建都要重做,只在无法换镜像、扩展也没有可用包时考虑。

自动启用扩展给所有新建数据库使用:把 SQL 文件放进 /docker-entrypoint-initdb.d(如 02-vector.sql),文件按名称排序执行,*.sql 以 POSTGRES_USER 身份运行。该目录只在数据目录为空时执行一次,之后不再运行,失败也不会重跑。

pgvector 升级到新镜像后,在每个已启用的数据库执行:

sql
ALTER EXTENSION vector UPDATE;
SELECT extversion FROM pg_extension WHERE extname = 'vector';

日常运维 ​

宿主机连接:psql -h 127.0.0.1 -p 5432 -U appuser -d appdb。容器内部的本地连接走 trust 免密,这是镜像初始化行为,不影响外部连接仍需口令。

查看日志与重启:

bash
docker compose logs -f postgres
docker compose restart postgres

docker compose stop 只停容器、保留卷。危险操作是 docker compose down -v:该命令连同命名卷 pgdata 一起删除,数据库文件随之不可恢复,执行前必须先备份(见下方数据与恢复边界)。

数据与恢复边界:恢复单元是命名卷 pgdata、.env 口令、compose.yaml 和 ./init 脚本目录。逻辑备份直接写入宿主机 backups/,不写入 PostgreSQL 数据目录:

bash
mkdir -p backups
docker compose exec -T postgres pg_dump -U postgres -d appdb -Fc \
  > "backups/appdb-$(date +%Y%m%d).dump"

定时备份(宿主机 crontab 每日凌晨 2 点,crontab 中 % 需转义为 \%):

cron
0 2 * * * cd /opt/postgres-vector && mkdir -p backups && docker compose exec -T postgres pg_dump -U postgres -d appdb -Fc > "backups/appdb-$(date +\%F).dump"

恢复到新库(先建库,再使用 pg_restore 导入):

bash
docker compose exec -T postgres createdb -U postgres appdb_restore
docker compose exec -T postgres psql -U postgres -d appdb_restore \
  -c 'CREATE EXTENSION IF NOT EXISTS vector;'
BACKUP_FILE="$(ls -1t backups/appdb-*.dump | head -n 1)"
docker compose exec -T postgres pg_restore -U postgres -d appdb_restore --exit-on-error \
  < "$BACKUP_FILE"
docker compose exec -T postgres psql -U postgres -d appdb_restore \
  -tAc "SELECT count(*) FROM documents"
# 1

定期抽样恢复一份备份,确认 documents 和向量表都能读取;文件系统级冷备份需要先 docker compose stop 停写,通用卷归档与恢复验收见镜像数据备份与恢复教程。

容器默认 /dev/shm 只有 64MB,并行查询或并行索引构建耗尽共享内存时报 ERROR: could not resize shared memory segment ... : No space left on device。上调 Compose 的 shm_size 即可;提高 maintenance_work_mem 加速 HNSW 构建时,shm_size 至少要等于 maintenance_work_mem。

升级与回滚 ​

版本来源固定在镜像 tag 上:pgvector/pgvector:0.8.6-pg18-bookworm 同时声明 PostgreSQL 和 pgvector 版本。升级是切换到新的版本化 tag、拉取镜像并重建容器:

bash
docker compose pull postgres
docker compose up -d --force-recreate

PostgreSQL 小版本升级(18.x 之间)或 pgvector 小版本升级(0.8.x 之间)都应先在测试环境验证,再修改 Compose 中的完整镜像 tag。重建后对每个启用扩展的库执行 ALTER EXTENSION vector UPDATE;。

升级后的业务验收至少覆盖:SELECT version() 为新版本、docker compose ps 恢复 healthy、核心检索 SQL 正常返回。通用 Compose 更新与回退操作见服务更新回滚教程。

大版本升级(例如 18 → 19)不能让新旧版本共用一个已初始化数据目录,两条官方路径:

  1. 逻辑迁移:升级前 pg_dump 全量导出,挂新卷用新版本镜像初始化后灌入;注意恢复前目标库要先 CREATE EXTENSION,否则含扩展类型的表无法灌入。
  2. 物理迁移:pg_upgrade。PostgreSQL 18 起数据目录布局为 /var/lib/postgresql/<大版本>/docker,卷挂在 /var/lib/postgresql 正是 pg_upgrade --link 快速路径的前提。

大版本切换不可逆:新版本一旦写入数据目录,回退只能依赖升级前的备份恢复。执行前必须完成一份 pg_dump 并验证可恢复。小版本回滚切换回旧的完整镜像 tag 并重建容器即可,若旧镜像拒绝启动新数据目录,同样走备份恢复。

生产环境安全加固 ​

  • 端口暴露按远程访问一节的最小面原则执行:默认回环,跨机走内网绑定或隧道,不要把 5432 暴露公网。
  • 不要把 POSTGRES_HOST_AUTH_METHOD 设为 trust:那会让任何能连到端口的客户端免密可连;镜像默认已是"非回环一律 scram-sha-256",保持不动。
  • 应用一律用普通账号(appuser)+ 表级授权,不要共用 postgres 超级用户。
  • 口令可用 Docker Secrets 注入:postgres 镜像的 _FILE 变量仅支持 POSTGRES_PASSWORD、POSTGRES_USER、POSTGRES_DB、POSTGRES_INITDB_ARGS 四项。
  • 备份文件 backup-*.sql 含全库数据,按敏感数据保管;.env 权限 600。
  • 使用预装镜像时仍应固定 tag,定期检查镜像来源和漏洞扫描结果,不要在生产环境临时切换到未验证的 tag。
  • 通用暴露面、权限与镜像供应链基线见生产环境安全教程。
  • 只有在需要自定义认证规则时才维护 postgresql.conf 和 pg_hba.conf;默认认证规则已经覆盖本机免密和远程 SCRAM 认证。

常见问题 ​

CREATE EXTENSION vector 报 extension "vector" is not available。 ​

pgvector/pgvector 镜像已经包含扩展二进制。先确认镜像 tag 与 PostgreSQL 大版本匹配,再在目标数据库执行 CREATE EXTENSION IF NOT EXISTS vector;已有数据卷不会自动补跑初始化脚本。

应用连不上数据库。 ​

按序排查:docker compose ps 是否 healthy;客户端到的是否为宿主机映射端口(容器内 5432 对外可能不同);连接串走回环还是内网 IP——非回环连接被 scram-sha-256 拦下时报口令认证错误,报 no pg_hba.conf entry 则是映射端口挂到了别的进程;宿主机 lsof -i :5432 确认监听方。

容器启动失败,日志有 initdb 权限错误。 ​

bind mount 场景下 Linux 宿主机目录属主与容器内 postgres 用户(UID 999)不一致所致:chown -R 999:999 宿主机目录,或改用命名卷。

改了 .env 里的口令或库名,重启后没生效。 ​

镜像环境变量只在空数据目录首次初始化时生效。口令用 ALTER USER ... WITH PASSWORD 修改;库名对应执行 CREATE DATABASE。

FATAL: database "appdb" does not exist ​

当前数据卷中不存在 appdb,需要使用初始化超级用户创建数据库;全新部署通常由 POSTGRES_DB=appdb 自动创建。

bash
# 创建已有数据卷缺失的数据库
docker compose exec -T postgres psql -U postgres -d postgres -c 'CREATE DATABASE appdb OWNER postgres;'

# 查看当前数据库信息
docker compose exec -T postgres psql -U postgres -d template1 -c '\l' -c '\du'

# 链接数据库
docker compose exec postgres psql -U postgres -d appdb

重建容器后数据消失。 ​

大概率卷挂错路径:17 及以下版本必须挂 /var/lib/postgresql/data,挂到 /var/lib/postgresql 时数据实际写入了运行时自动创建的匿名卷。用 docker inspect <容器> --format '{{json .Mounts}}' 检查是否存在非预期的匿名卷;恢复只能依赖备份。

首次启动的初始化脚本没执行完。 ​

/docker-entrypoint-initdb.d 只在数据目录为空时整体执行一次;一个脚本失败会导致容器退出,编排器带着已初始化的数据目录重启后,剩余脚本不会继续。看日志 running /docker-entrypoint-initdb.d/... 停在哪个文件,修复后如数据目录已被部分写入,需要清卷重来。

时间显示差 8 小时。 ​

服务端默认 Etc/UTC,按远程访问与时区一节加 command: ["postgres", "-c", "timezone=Asia/Shanghai"];长期方案优先用 timestamptz 列存绝对时间。

带 WHERE 过滤的最近邻查询返回不足 LIMIT 条。 ​

近似索引先扫索引后过滤,属于机制而非故障,按调优一节开启 hnsw.iterative_scan。

could not resize shared memory segment。 ​

默认 /dev/shm 64MB 不够,上调 shm_size;并行 HNSW 构建场景下 shm_size 不小于 maintenance_work_mem。

镜像拉取超时。 ​

守护进程配置 registry mirror 后重试;更换加速源不改变镜像引用,拉取后比对 digest 确认内容一致。

总结 ​

本文使用版本化 pgvector/pgvector:0.8.6-pg18-bookworm,以 Compose、命名卷和初始化脚本部署 PostgreSQL 18.6 与 pgvector 0.8.6。正式使用前应验证容器健康、扩展版本、应用账号权限和重建后的数据;大版本升级或回退必须依赖已经验证可恢复的 pg_dump 备份。

参考资料 ​

全部公开文章由同一个站点构建和发布。