farmOS 4.0.9 Docker Compose 部署指南

Docker Compose 部署 farmOS 4.0.9 官方镜像与 PostgreSQL 17 数据库栈:部署步骤、持久化与销毁重建恢复闭环实测、环境变量与文件挂载、drush 运维命令与常见问题。

farmOS自托管2026/10/23

farmOS 4.0.9 Docker Compose 部署指南

目标:用 Docker Compose 在 Linux 服务器上部署 farmOS 4.0.9 官方镜像与 PostgreSQL 17 数据库栈,完成 Web 安装向导、数据持久化验证与「销毁重建数据不丢」的恢复闭环,并整理日常运维命令。全部命令与回显均在 Docker 29.8.0 + Compose v5.5.1 环境实测采集(实测日期:2026-10-23)。

一、环境信息

  • 操作系统:Linux 服务器(Ubuntu 22.04 系实测)
  • Docker:29.8.0(docker --version),Compose 插件:v5.5.1(docker compose version)
  • farmOS 镜像:farmos/farmos:4.0.9(1.58GB,官方 Docker Hub 镜像,内含 Apache + PHP 8.4.27 + 完整代码库)
  • 数据库镜像:postgres:17(640MB,官方 compose 示例同款)
  • 部署目录:/opt/farmos-stack/(compose 项目名 farmos-409-stack)
  • 端口:宿主机 80 → farmOS Web(生产建议前置反向代理做 TLS,见官方 Requirements)
  • 镜像内置验证(容器内实测):php -v 回显 PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:29) (NTS),与 4.0.9 更新日志"官方镜像升级 PHP 8.4.27 修复 CVE-2026-109618/109619"一致

💡 说明:官方镜像已封装 farmOS 全部 Web 依赖(PHP 8.4 + BCMath/EXIF/GEOS/SimpleXML + Apache),这是官方推荐的托管方式——不需要像裸机部署那样自己编译 GEOS 扩展(对比见《Ubuntu 22.04 安装 farmOS 4.0.9 指南》3.2.3 节)。

二、镜像选型说明

官方镜像发布在 Docker Hub:hub.docker.com/r/farmos/farmos,官方部署文档明确两点:

  1. 必须锁版本:镜像标签用具体版本号(本文 4.0.9),官方明示"不建议使用 latest 标签,因为更新需要手动步骤"(数据库更新不会自动执行);
  2. 镜像与预打包 tarball 同源:官方 tarball 就是从镜像内 /opt/drupal 打包而来,两条路线代码一致。

镜像内关键路径:代码库 /opt/drupal,文档根 /opt/drupal/web,站点数据 /opt/drupal/web/sites(Web 安装向导与上传文件都落在这里),OAuth2 密钥目录 /opt/drupal/keys。进程用户 www-data(UID/GID 33),bind mount 的宿主目录需要归属 33:33。

数据库选型:官方 docker-compose.production.yml 示例配 PostgreSQL 17(farmOS 官方推荐的数据库)。也可换成外部 MySQL/MariaDB(farmOS 支持 MySQL 8.0+/MariaDB 10.6+),或在示例栈外自备数据库并删除 db 服务。

三、前置条件

  • 服务器已安装 Docker 29+ 与 Compose 插件(docker compose version 可用);
  • 服务器 80 端口空闲(或改映射端口);
  • 能访问 Docker Hub 拉取镜像(内网环境可走代理或私有镜像仓库);
  • 磁盘余量 ≥ 5GB(镜像 2.2GB + 数据库与站点数据)。

四、部署步骤

4.1 准备目录与 compose 文件

mkdir -p /opt/farmos-stack/{sites,keys,db}
cd /opt/farmos-stack
# 官方要求:sites 目录属主必须是容器内 www-data(UID 33),否则安装器写不进 settings.php
sudo chown 33:33 sites keys

创建 docker-compose.yml(源自官方 docker/docker-compose.production.yml,把版本号改为 4.0.9 并加显式项目名与 php.ini 挂载):

name: farmos-409-stack

services:
  db:
    image: postgres:17
    volumes:
      - './db:/var/lib/postgresql/data'
    environment:
      # 生产环境请改成强密码,并按需更换用户/库名
      POSTGRES_USER: farm
      POSTGRES_PASSWORD: farm
      POSTGRES_DB: farm
    restart: always

  www:
    depends_on:
      - db
    image: farmos/farmos:4.0.9
    volumes:
      - './sites:/opt/drupal/web/sites'
      - './keys:/opt/drupal/keys'
      - './php.ini:/usr/local/etc/php/conf.d/farmos.ini'
    ports:
      - '80:80'
    restart: always

同目录创建 php.ini(上传上限按需调整,官方 Customizing PHP 一节的做法):

upload_max_filesize = 50M
post_max_size = 50M

逐项说明:db 服务用 bind mount ./db 持久化 PGDATA;www 服务把 sites(设置+上传文件)与 keys(OAuth2 密钥)持久化到宿主,php.ini 以只读方式挂进 PHP 的 conf.d;depends_on 保证数据库先起;restart: always 让两服务开机自启并崩溃自愈。

4.2 启动栈

docker compose up -d

实测回显:

 Container farmos-409-stack-db-1 Started
 Container farmos-409-stack-www-1 Starting
 Container farmos-409-stack-www-1 Started

确认状态(实测回显):

docker compose ps
NAME                     STATUS
farmos-409-stack-db-1    Up 12 seconds
farmos-409-stack-www-1   Up 12 seconds

镜像体积核对(docker compose images 实测回显节选):

farmos-409-stack-db-1    postgres       17   linux/amd64   640MB
farmos-409-stack-www-1   farmos/farmos  4.0.9  linux/amd64  1.58GB

4.3 Web 安装向导

浏览器访问 http://<服务器IP>/,自动 302 到安装器(实测:curl -s -o /dev/null -w "%{http_code} %{redirect_url}" http://localhost/ 回显 302 /core/install.php)。farmOS 只有英文界面与 farm Profile,语言/Profile 步自动跳过,直达 Database configuration 页。按 compose 里的环境变量填:

字段取值对应 compose 配置
驱动PostgreSQL—
Database namefarmPOSTGRES_DB: farm
Database usernamefarmPOSTGRES_USER: farm
Database passwordfarmPOSTGRES_PASSWORD: farm
Hostdbcompose 服务名(容器 DNS)
Port5432PG 默认端口

⚠️ 注意:Host 一定要填 db(compose 服务名),不能填 127.0.0.1——farmOS 在自己的容器里,127.0.0.1 指向它自己。填错会报数据库连接失败。

点击 Save and continue 后进入 Installing farmOS 批量安装(数分钟),随后是 Configure site 页:站点名(实测 farmOS Compose)、站点邮箱 admin@example.com、管理员账号 admin / 密码 q1w2e3r4、时区 UTC,提交后 302 回首页。

4.4 部署结果验证

验证 1:Web 可用。 登录后首页标题(实测):

curl -s http://localhost/ | grep -o "<title>[^<]*"
<title>Dashboard | farmOS Compose

验证 2:站点数据已落宿主目录。 站点关键文件出现在宿主 sites/ 下(实测 ls sites/default/):

default.services.yml
default.settings.php
files
settings.php

settings.php 内含安装向导写入的数据库连接(host 为 db),这是后续恢复与升级的命根子。

验证 3:容器内 drush 可用。

docker compose exec www drush status --root=/opt/drupal | grep -E "Drupal version|DB driver|Database "

实测回显:

Drupal version   : 11.3.19
DB driver        : pgsql
Database         : Connected

五、数据持久化说明

farmOS 容器分两类数据,持久化策略完全不同:

数据容器内路径持久化方式丢失后果
站点设置与上传文件/opt/drupal/web/sitesbind mount → 宿主 ./sitessettings.php 丢失=重新安装向导
OAuth2 密钥/opt/drupal/keysbind mount → 宿主 ./keys需重新生成密钥对
数据库/var/lib/postgresql/databind mount → 宿主 ./db全部业务数据丢失
代码库/opt/drupal(sites/keys 除外)不持久化(跟随镜像版本)无需持久化,升级即换镜像

官方文档强调:容器里 /opt/drupal/web/sites 之外的一切在容器销毁后都会被镜像内容替换——这正是「代码跟随镜像、数据跟随宿主」的设计。

恢复闭环实测:把整个栈销毁再重建(模拟服务器重启/容器升级):

docker compose down
docker compose up -d
sleep 15
docker compose exec www drush status --root=/opt/drupal | grep -E "DB driver|Database "

实测回显:

 Container farmos-409-stack_default  Removed
 Container farmos-409-stack-www-1  Started
DB driver        : pgsql
Database         : Connected

再从宿主访问登录页(实测回显):

curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8147/user/login
200

站点直接可用、无需重跑安装向导——sites 与 db 两个 bind mount 在容器销毁后完整保留了设置与数据,恢复闭环通过。

六、环境变量表

变量位置示例值说明
POSTGRES_USERdb 服务farm数据库超级用户名,与向导 Database username 一致
POSTGRES_PASSWORDdb 服务farm数据库密码,生产必改强口令
POSTGRES_DBdb 服务farm初始化创建的库名,与向导 Database name 一致
depends_onwww 服务db启动顺序:先库后站

💡 说明:farmOS 官方镜像本身没有业务环境变量——数据库连接、站点配置全部由 Web 安装向导写入 sites/default/settings.php(随 bind mount 持久化)。镜像的可定制点只有 PHP 运行参数(挂 php.ini)与站点/密钥目录(挂 sites/keys)。

七、配置参数说明与文件挂载

  • ./sites:/opt/drupal/web/sites:必挂。含 default/settings.php(数据库连接、hash_salt、config 同步目录)、default/files/(公开上传文件)、private/(私有文件,需按官方文档在 settings.php 加 file_private_path 后创建);
  • ./keys:/opt/drupal/keys:启用 farmOS API OAuth2 时需要。在后台 Configuration > Web Services > Consumers 的 Settings 页把公私钥路径设为 ../keys/public.key 与 ../keys/private.key 并生成,宿主目录属主需为 33:33;
  • ./php.ini:/usr/local/etc/php/conf.d/farmos.ini:PHP 运行参数覆盖。本文挂载的 50M 上传限制实测生效(容器内 php -i | grep upload_max_filesize 回显 upload_max_filesize => 50M => 50M,post_max_size => 50M => 50M);
  • ./db:/var/lib/postgresql/data:PGDATA。注意 PG 17 的 data 目录挂载后由 postgres 用户管理,不要手工改属主。

八、等价 docker run 命令

不使用 Compose 时的官方等价单容器命令(仅 Web,不含数据库):

mkdir sites && sudo chown 33:33 sites
docker run --rm -p 80:80 -v "${PWD}/sites:/opt/drupal/web/sites" farmos/farmos:4.0.9

与 Compose 栈的差异:docker run 单容器没有数据库、没有 restart 策略、没有 keys/php.ini 挂载,只适合临时体验。生产一律用 Compose 栈或自备外部数据库。

九、常用运维命令

# 状态查看
docker compose ps
# 日志跟踪(数据库与 Web 分开看)
docker compose logs -f www
docker compose logs -f db
# 重启(数据不受影响)
docker compose restart
# 停止并移除容器与网络(bind mount 数据保留)
docker compose down
# 修改 compose 文件或镜像版本后应用
docker compose up -d

farmOS 侧命令统一用 docker compose exec www drush ...(实测可用):

# 例行维护(建议宿主 crontab 每周触发一次)
docker compose exec www drush cron
# 清缓存
docker compose exec www drush cr
# 数据库更新(升级后必跑)
docker compose exec www drush updatedb -y
# 生成管理员一次性登录链接(实测输出示例)
docker compose exec www drush uli --uid=1

drush uli 实测回显:

http://default/user/reset/1/1792731154/mtdX6t1NZHFQTRw2pJFgHcUchY-ZeXsy-WDTZu0tBII/login

⚠️ 注意:链接里的主机名是 default(drush 在容器内感知不到对外域名),把域名部分替换成你的站点地址即可使用;一次性链接有时效。

备份与升级:

# 备份:数据库导出 + 站点目录打包
docker compose exec db pg_dump -U farm farm > farm-$(date +%F).sql
tar czf farm-sites-$(date +%F).tar.gz sites keys
# 升级:改镜像版本号(如 farmos/farmos:4.0.10)后
docker compose down
docker compose up -d
docker compose exec www drush updatedb -y
docker compose exec www drush cr

升级要点(官方 Updating 一节):down 彻底移除旧容器,up -d 用新镜像重建;sites 挂载目录里的 settings.php 与文件全部保留;升级后必须跑 drush updatedb 与 drush cr。

十、常见问题(FAQ)

Q1:ghcr.io 能不能拉 farmOS? 本文实测 docker pull ghcr.io/farmos/farmos:4.0.9 返回 denied——farmOS 官方镜像发布在 Docker Hub(farmos/farmos),官方文档引用的也是 Docker Hub 地址,请勿从 ghcr 拉取。

Q2:启动后 sites 目录写入失败 / 向导无法写 settings.php? bind mount 的宿主目录属主不对。官方要求 sites 目录属主为 UID 33(容器内 www-data):sudo chown 33:33 sites 后重启容器再试。

Q3:向导数据库步骤报连接失败? 按顺序排查:Host 是否填的 db(服务名);compose 里 POSTGRES_PASSWORD 与向导密码是否一致;db 容器是否真的 Ready(docker compose logs db 看到 database system is ready to accept connections 再刷新向导)。

Q4:容器重建后又回到了安装向导? sites 挂载丢失或被清空(settings.php 没了)。检查 compose 文件 volumes 段与宿主目录内容;只要 ./sites/default/settings.php 还在,重建容器就不会触发重装。

Q5:down 之后数据会丢吗? 不会。down 只删容器与网络,三个 bind mount(sites/keys/db)都在宿主目录里;实测 down → up 后站点直接可用(见第五节恢复闭环)。会丢数据的操作只有 rm -rf ./sites ./db 这类删目录行为。

Q6:cron 邮件报错 "Unable to send email"? 与裸机部署同理:栈内没有 MTA。drush cron 的维护任务本身已执行,邮件发送失败仅写入日志。需要通知能力时接入 SMTP 模块或外置邮件服务。

十一、参考链接