PixelFed 0.15.2 Docker Compose 部署指南

官方编排形态从零部署 PixelFed v0.15.2 全流程实测:MariaDB 换型、自动迁移、管理员创建、功能验证与 8 类故障排障实录

PixelFedPixelFedDocker Compose自托管2026/10/27

PixelFed 0.15.2 Docker Compose 部署指南

目标:在一台 Ubuntu 22.04 服务器上,用官方编排形态从零部署 PixelFed v0.15.2(联邦图片分享平台),覆盖 .env 配置、数据库选型(含 MySQL 9 迁移失败的实测换型)、自动迁移、管理员创建、图片/故事功能验证与五大典型故障排查。已在 Ubuntu 22.04 x86_64 服务器(IP 192.168.146.128,端口 8135)实测通过,登录、发帖、故事发布全部验证成功。

一、环境信息

项目实测值
操作系统Ubuntu 22.04(x86_64)
Docker29.9.2, build a82e528
Docker Composev5.6.1
PixelFedv0.15.2(ghcr.io/pixelfed/pixelfed:v0.15.2,2.08GB)
数据库MariaDB 11.8(458MB)
缓存/队列Redis 8(redis:8-alpine,161MB)
部署目录/opt/pixelfed
服务端口8135(HTTP)
编排栈名pixelfed-0.15.2-stack(内部网络段 10.89.65.0/24)
演示账号admin / q1w2e3r4(管理员)

本方案为 5 角色完整形态:应用(Web/API)+ Horizon 队列工作器 + 定时任务调度 + MariaDB + Redis。

二、镜像选型说明

2.1 应用镜像来源

PixelFed 官方发布应用包于 GitHub Container Registry:ghcr.io/pixelfed/pixelfed:v0.15.2。官方仓库根目录同时提供 docker-compose.yml(该文件内以源码构建方式定义应用),对使用官方预构建发行包的部署,官方文档推荐直接引用 GHCR 上的版本号发行包。本文采用「官方预构建发行包 v0.15.2」,版本固定、可重复部署。

# 拉取官方发行包(预构建应用)
docker pull ghcr.io/pixelfed/pixelfed:v0.15.2

实测回显(首次拉取后再次执行验证,输出首尾节选):

v0.15.2: Pulling from pixelfed/pixelfed
Digest: sha256:f1237913ef0d392a5afef1dab8e3dd01234435ea90314a3a7221a2ea00932132
Status: Image is up to date for ghcr.io/pixelfed/pixelfed:v0.15.2
ghcr.io/pixelfed/pixelfed:v0.15.2

2.2 数据库选型:必须用 MariaDB 11.x,不要用 MySQL 9

官方编排文件开头注释为「## MariaDB and Redis (optional)」,但其 database 服务默认写的是 mysql:9。实测 v0.15.2 的 268 个迁移中,Laravel Pulse 迁移会创建 key_hash 生成列,MySQL 9 拒绝在生成列中使用 md5(),迁移直接失败(复现与解决见 2.3);MariaDB 11.8 执行同一建表语句正常。本方案将 database 服务改为 mariadb:11.8,全量迁移实测通过。

2.3 依赖组件版本对照(实测)

组件镜像/版本实测体积角色
应用ghcr.io/pixelfed/pixelfed:v0.15.22.08GBWeb/API + 队列工作器 + 调度器(同一发行包三个服务)
数据库mariadb:11.8458MB全量数据存储
缓存redis:8-alpine161MB缓存/会话/队列

💡 技术拓展:关系型数据库选型 PixelFed 官方支持 MySQL 系与 PostgreSQL——

数据库特点适用场景
MariaDB 11.x与 MySQL 协议兼容,生成列函数限制宽松官方编排同源,实测 268 迁移全过(本文采用)
MySQL 8.4生成列函数校验同样收紧,需实测迁移已有 MySQL 8.4 集群复用
MySQL 9.x生成列禁用 md5(),Pulse 迁移失败(实测)不推荐
PostgreSQL 16官方支持,Pulse 用 uuid+md5 表达式已有 PG 运维体系

本文因官方编排默认分支 + 实测结论采用 MariaDB 11.8;已有 PostgreSQL 运维体系的团队可改用 PG(.env 将 DB_CONNECTION 改为 pgsql)。

三、前置条件

  1. 服务器已安装 Docker 29.x 与 Docker Compose v2+(docker --version、docker compose version 可用);
  2. 端口 8135 未被占用(可用 ss -ltn | grep 8135 检查);
  3. 磁盘空间 ≥ 10GB(应用发行包 2.08GB + 数据库与媒体数据增长);
  4. 内存 ≥ 2GB(应用 + Horizon 队列 + MariaDB 并存运行);
  5. 明确实例的对外访问地址:本文用 http://192.168.146.128:8135(生产带域名的场景,把下文所有 192.168.146.128 替换为你的域名,并在反向代理层做 TLS)。

四、部署步骤

4.1 目录与权限准备

本节创建部署目录,并预先解决「存储目录属主」问题(应用发行包内运行用户为 www-data,UID=33;存储目录若由 root 创建并挂载,应用启动会因无写权限失败,现象与解决见 11.1/11.2)。

# 创建部署目录与存储目录
mkdir -p /opt/pixelfed && cd /opt/pixelfed
mkdir -p storage
# 将存储目录属主对齐发行包内运行用户 www-data(UID/GID=33)
chown -R 33:33 /opt/pixelfed/storage

验证属主已生效:

ls -ld /opt/pixelfed/storage

实测回显:

drwxr-xr-x 2 33 33 4096 Oct 27 00:38 /opt/pixelfed/storage

4.2 编写 .env 配置文件

PixelFed 的全部行为由 .env 环境变量驱动。基于官方 .env.example(v0.15.2,88 行)修改,以下是本方案实测使用的完整 .env(数据库密码演示用 q1w2e3r4,生产请换强密码):

APP_NAME="Pixelfed"
APP_ENV="production"
APP_KEY=
APP_DEBUG="false"
APP_LOCALE="en-US"

# Instance Configuration
OPEN_REGISTRATION="false"
ENFORCE_EMAIL_VERIFICATION="false"
PF_MAX_USERS="1000"
PF_MIN_REGISTRATION_AGE="16"
OAUTH_ENABLED="true"
OAUTH_PAT_ENABLED="false"
ENABLE_CONFIG_CACHE="true"
INSTANCE_DISCOVER_PUBLIC="true"
STORIES_ENABLED="true"

# Media Configuration
IMAGE_DRIVER=vips # vips, gd, imagick
PF_OPTIMIZE_IMAGES="true"
IMAGE_QUALITY="80"
MAX_PHOTO_SIZE="15000"
MAX_CAPTION_LENGTH="500"
MAX_ALBUM_LENGTH="4"

# Instance URL Configuration
APP_URL="http://192.168.146.128:8135"
APP_DOMAIN="192.168.146.128"
ADMIN_DOMAIN="192.168.146.128"
SESSION_DOMAIN=null
TRUST_PROXIES="*"
FORCE_HTTPS_URLS="false"
SESSION_SECURE_COOKIE="false"

# Database Configuration
DB_CONNECTION="mysql"
DB_HOST="db"
DB_PORT="3306"
DB_DATABASE="pixelfed"
DB_USERNAME="pixelfed"
DB_PASSWORD="q1w2e3r4"
DB_ROOT_PASSWORD="q1w2e3r4"

# Redis Configuration
REDIS_CLIENT="predis"
REDIS_SCHEME="tcp"
REDIS_HOST="redis"
REDIS_PASSWORD="null"
REDIS_PORT="6379"

# Laravel Configuration
SESSION_DRIVER="database"
CACHE_STORE="redis"
QUEUE_DRIVER="redis"
BROADCAST_DRIVER="log"
LOG_CHANNEL="stack"
HORIZON_PREFIX="horizon-"

# ActivityPub Configuration
ACTIVITY_PUB="false"
AP_REMOTE_FOLLOW="false"
AP_INBOX="false"
AP_OUTBOX="false"
AP_SHAREDINBOX="false"

# Experimental Configuration
EXP_EMC="true"

## Mail Configuration (Post-Installer)
MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="pixelfed@example.com"
MAIL_FROM_NAME="Pixelfed"
MAIL_AUTO_TLS=true

## S3 Configuration (Post-Installer)
PF_ENABLE_CLOUD=false
FILESYSTEM_CLOUD=s3

关键项说明(全量 .env 变量的逐项实测说明见本专栏配置参数详解篇):

  • APP_URL / APP_DOMAIN / ADMIN_DOMAIN:三者必须与实际访问地址一致。PixelFed 按 APP_DOMAIN 做路由域名约束,不一致会整站 404(见 11.5 实测);
  • FORCE_HTTPS_URLS:默认开启,强制生成 https 链接。纯 HTTP 内网部署必须显式置 false(见 11.4);
  • SESSION_SECURE_COOKIE:默认 true,HTTP 部署时浏览器不会回传会话 Cookie,导致「登录永远失败」,必须置 false(见 11.4);
  • DB_ROOT_PASSWORD:官方 .env.example 未含此变量,但编排健康检查引用它,缺失会导致数据库初始化失败,必须补上;
  • STORIES_ENABLED:故事功能开关,默认关闭;注意它受「配置缓存」固化,改动后需清配置缓存(见 11.6)。

4.3 生成 APP_KEY

Laravel 应用必须有 APP_KEY(用于会话与加密)。用应用发行包内自带的 artisan 一次性生成并填入 .env:

docker run --rm ghcr.io/pixelfed/pixelfed:v0.15.2 php artisan key:generate --show

实测回显(每次生成不同,取自己那次输出的完整值):

base64:bm8hWRcRAgQ9LFRyvDgraqvX1J23z2mSht7dKIp3n70=

将输出填入 .env 的 APP_KEY= 后(本环境实测生成值为 base64:bm8hWRcRAgQ9LFRyvDgraqvX1J23z2mSht7dKIp3n70=,读者须使用自己环境生成的值,不可照抄)。

4.4 编写 docker-compose.yml 编排文件

在 /opt/pixelfed 下创建 docker-compose.yml(基于官方 v0.15.2 docker-compose.yml,改用 GHCR 发行包与 MariaDB 11.8,并修正两处官方编排与发行包不匹配的健康检查):

name: pixelfed-0.15.2-stack

services:
## MariaDB and Redis
  db:
    image: mariadb:11.8
    restart: unless-stopped
    env_file:
      - .env
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - ./mariadb-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s
    networks:
      - pixelfed-network

  redis:
    image: redis:8-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - ./redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 10s
    networks:
      - pixelfed-network

## Pixelfed containers - App (Web/API), Horizon Queue, Scheduled task.
  pixelfed:
    image: ghcr.io/pixelfed/pixelfed:v0.15.2
    restart: unless-stopped
    ports:
      - "8135:8080"
    env_file:
      - .env
    environment:
      HEALTHCHECK_PATH: "/up"
      LOG_OUTPUT_LEVEL: "error"
      SSL_MODE: "off"
      PHP_POST_MAX_SIZE: "500M"
      PHP_UPLOAD_MAX_FILE_SIZE: "500M"
      PHP_OPCACHE_ENABLE: "1"
      PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
      AUTORUN_ENABLED: "true"
      AUTORUN_LARAVEL_MIGRATION: "true"
      AUTORUN_LARAVEL_MIGRATION_ISOLATION: "true"
      AUTORUN_LARAVEL_STORAGE_LINK: "true"
      AUTORUN_LARAVEL_EVENT_CACHE: "true"
      AUTORUN_LARAVEL_ROUTE_CACHE: "true"
      AUTORUN_LARAVEL_VIEW_CACHE: "true"
      AUTORUN_LARAVEL_CONFIG_CACHE: "true"
    volumes:
      - ./storage:/var/www/html/storage
    depends_on:
      - db
      - redis
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/api/v1/instance -o /dev/null"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s
    networks:
      - pixelfed-network

  horizon:
    image: ghcr.io/pixelfed/pixelfed:v0.15.2
    restart: unless-stopped
    command: ["php", "artisan", "horizon"]
    env_file:
      - .env
    environment:
      PHP_POST_MAX_SIZE: "500M"
      PHP_UPLOAD_MAX_FILE_SIZE: "500M"
      PHP_OPCACHE_ENABLE: "1"
      PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
      AUTORUN_ENABLED: "false"
      AUTORUN_LARAVEL_MIGRATION: "false"
      AUTORUN_LARAVEL_MIGRATION_ISOLATION: "false"
      AUTORUN_LARAVEL_STORAGE_LINK: "true"
      AUTORUN_LARAVEL_EVENT_CACHE: "false"
      AUTORUN_LARAVEL_ROUTE_CACHE: "false"
      AUTORUN_LARAVEL_VIEW_CACHE: "false"
      AUTORUN_LARAVEL_CONFIG_CACHE: "true"
    volumes:
      - ./storage:/var/www/html/storage
    depends_on:
      - db
      - redis
    healthcheck:
      test: ["CMD", "healthcheck-horizon"]
      interval: 10s
      timeout: 5s
      retries: 3
    networks:
      - pixelfed-network

  scheduler:
    image: ghcr.io/pixelfed/pixelfed:v0.15.2
    restart: unless-stopped
    command: ["php", "artisan", "schedule:work"]
    stop_signal: SIGTERM
    env_file:
      - .env
    environment:
      PHP_POST_MAX_SIZE: "500M"
      PHP_UPLOAD_MAX_FILE_SIZE: "500M"
      PHP_OPCACHE_ENABLE: "1"
      PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
      AUTORUN_ENABLED: "false"
      AUTORUN_LARAVEL_MIGRATION: "false"
      AUTORUN_LARAVEL_MIGRATION_ISOLATION: "false"
      AUTORUN_LARAVEL_STORAGE_LINK: "true"
      AUTORUN_LARAVEL_EVENT_CACHE: "false"
      AUTORUN_LARAVEL_ROUTE_CACHE: "false"
      AUTORUN_LARAVEL_VIEW_CACHE: "false"
      AUTORUN_LARAVEL_CONFIG_CACHE: "true"
    volumes:
      - ./storage:/var/www/html/storage
    depends_on:
      - db
      - redis
    healthcheck:
      test: ["CMD", "healthcheck-schedule"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    networks:
      - pixelfed-network

networks:
  pixelfed-network:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.65.0/24

⚠️ 两处与官方编排的差异(实测原因):

  1. database 服务由 mysql:9 改为 mariadb:11.8(MySQL 9 迁移失败,见 2.2/11.3);
  2. 应用服务健康检查官方写法 healthcheck-frankenphp 在 v0.15.2 发行包内不存在,改用 API 探活;数据库健康检查 mysqladmin 在 MariaDB 11.8 中改名为 mariadb-admin(均见 11.7)。

4.5 启动与自动迁移

cd /opt/pixelfed
docker compose up -d

实测回显:

[+] Running 6/6
 ✔ Network pixelfed-0152-stack_pixelfed-network  Created
 ✔ Container pixelfed-0.15.2-db      Started
 ✔ Container pixelfed-0.15.2-redis   Started
 ✔ Container pixelfed-0.15.2-app     Started
 ✔ Container pixelfed-0.15.2-horizon Started
 ✔ Container pixelfed-0.15.2-scheduler Started

首次启动时,应用服务会自动执行初始化序列(AUTORUN):storage:link → 清缓存 → 数据库连接检查 → 全量迁移 → 优化缓存。观察启动日志:

docker compose logs pixelfed | grep -E 'storage|migration|migrat|optimize|cache|Server running' | head -20

实测回显(输出节选,实测首尾):

pixelfed-0.15.2-app  | Starting container ...
pixelfed-0.15.2-app  | Pixelfed auto-run
pixelfed-0.15.2-app  |  * Running in /var/www/html
pixelfed-0.15.2-app  |  * Setting Laravel storage permissions ... done!
pixelfed-0.15.2-app  |  * Clearing Laravel caches ... done!
pixelfed-0.15.2-app  |  * Attempting to establish a database connection ...
pixelfed-0.15.2-app  |  * Running Laravel migrations ...
pixelfed-0.15.2-app  | INFO  Running migrations.
pixelfed-0.15.2-app  |  2014_10_12_000000_create_password_resets_tables ............ DONE
pixelfed-0.15.2-app  |  (……268 个迁移逐条输出,见 migrate:status 核对……)
pixelfed-0.15.2-app  |  * Caching Laravel config ... done!
pixelfed-0.15.2-app  |  * Caching Laravel routes ... done!
pixelfed-0.15.2-app  |  * Caching Laravel views ... done!
pixelfed-0.15.2-app  |  *** Pixelfed Auto-run completed successfully ***

等待全部服务健康(首次迁移约 1–2 分钟):

docker compose ps

实测回显:

NAME                    STATUS
pixelfed-0.15.2-app     Up 9 minutes (healthy)
pixelfed-0.15.2-db      Up 9 minutes (healthy)
pixelfed-0.15.2-horizon Up 9 minutes (healthy)
pixelfed-0.15.2-redis   Up 9 minutes (healthy)
pixelfed-0.15.2-scheduler Up 9 minutes (healthy)

迁移核对(数量应为 268):

docker compose exec pixelfed php artisan migrate:status | grep -c 'Ran'

实测回显:

268

4.6 创建管理员与初始化命令

官方发行包不带默认账号,需用 artisan 命令创建。演示账号 admin / q1w2e3r4:

docker compose exec pixelfed php artisan user:create \
  --name="admin" --username="admin" \
  --email="admin@example.com" \
  --password="q1w2e3r4" \
  --is_admin=1 --confirm_email=1

实测回显:

Created user admin!

⚠️ 注意:--is_admin=1 仅在创建时给出标记,实测 users.is_admin 仍为 false,需再用 user:admin 授予管理权限(交互确认):

docker compose exec pixelfed php artisan user:admin admin

实测回显(交互输入 yes):

 Add admin privileges to this user? (yes/no) [no]:
 > yes

Successfully changed permissions!

继续两条官方推荐的初始化命令(城市数据用于地点/位置功能;实例 Actor 是实例自身的联邦身份记录):

# 1) 导入城市数据:发行包不含 cities.json,需先从源码仓库放入存储目录(见 11.8)
docker compose exec pixelfed php artisan import:cities

实测回显:

imported 128769 entries!
# 2) 创建实例 Actor
docker compose exec pixelfed php artisan instance:actor

实测回显:

Created instance actor!

4.7 部署验证

依次验证 API、登录页、管理后台三个入口:

# 1) 实例 API(用配置的 IP/域名访问,不要用 127.0.0.1,原因见 11.5)
curl -s http://192.168.146.128:8135/api/v1/instance -H 'Accept: application/json' | head -c 200

实测回显(输出节选):

{"domain":"192.168.146.128","title":"Pixelfed","version":"3.5.3 (compatible; Pixelfed 0.15.2)","description":...
# 2) 登录页
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.146.128:8135/login

实测回显:

200

用浏览器访问 http://192.168.146.128:8135/login,以 admin / q1w2e3r4 登录,应跳转至 Web 客户端;再以同一账号发布图片帖、创建故事,即可完成功能级验证(本环境实测:三图相册帖发布成功,故事发布成功并在个人主页故事栏展示)。

五、数据持久化说明

编排卷(相对 /opt/pixelfed)用途重建栈后是否保留
./mariadb-data数据库全部数据(用户/帖子/关注/配置等 268 张表)是
./redis-data缓存/队列(AOF 持久化,appendonly yes)是
./storage用户上传媒体、缩略图、日志、OAuth 密钥(storage/oauth-*.key)是

⚠️ 注意:OAuth 公私钥(storage/oauth-public.key / oauth-private.key)存放在 storage 卷中。若换用空的存储卷重新部署,务必执行 php artisan passport:keys 重新生成,否则已颁发的 OAuth 令牌全部失效。备份策略建议:每日 mariadb-dump 整库 + tar 打包 ./storage(日常维护命令详见本专栏命令详解篇)。

六、环境变量表

部署即用到的核心变量(完整全量逐项说明见配置详解篇):

变量实测取值说明
APP_NAMEPixelfed站点名称
APP_ENVproduction运行环境
APP_KEYbase64:...(4.3 生成)加密密钥,必须唯一
APP_URLhttp://192.168.146.128:8135对外访问地址
APP_DOMAIN / ADMIN_DOMAIN192.168.146.128路由域名约束
FORCE_HTTPS_URLSfalseHTTP 部署必须关(默认 true)
SESSION_SECURE_COOKIEfalseHTTP 部署必须关(默认 true)
OPEN_REGISTRATIONfalse是否开放注册
STORIES_ENABLEDtrue故事功能开关(默认 false)
DB_CONNECTION / DB_HOSTmysql / db数据库类型与编排内主机名
DB_DATABASE / DB_USERNAME / DB_PASSWORDpixelfed / pixelfed / q1w2e3r4业务库凭据
DB_ROOT_PASSWORDq1w2e3r4数据库 root 口令(健康检查引用)
REDIS_HOST / REDIS_PORTredis / 6379Redis 地址
IMAGE_DRIVERvips图片处理驱动(vips/gd/imagick)
MAX_ALBUM_LENGTH4单帖图片上限
MAX_CAPTION_LENGTH500文案上限(按 Unicode 字符计数)
ACTIVITY_PUBfalse联邦总开关(开启后强制 https)

七、配置参数说明

除 .env 外,应用服务还接受编排 environment: 注入的发行包级变量:

变量实测取值说明
SSL_MODEoff发行包内 Web 服务(Caddy/FrankenPHP)的 TLS 模式;off=明文 HTTP
PHP_POST_MAX_SIZE / PHP_UPLOAD_MAX_FILE_SIZE500MPHP 上传上限,需大于单图上限 MAX_PHOTO_SIZE
AUTORUN_ENABLEDtrue启动自动初始化总开关
AUTORUN_LARAVEL_MIGRATIONtrue启动自动迁移(仅应用服务开启)
AUTORUN_LARAVEL_CONFIG_CACHE / ROUTE_CACHE / VIEW_CACHE / EVENT_CACHEtrue启动时重建各类缓存
LOG_OUTPUT_LEVELerror发行包 JSON 日志级别

修改 .env 后的生效方式:docker compose up -d(重建服务,AUTORUN 会重建缓存)。特别注意:STORIES_ENABLED、OPEN_REGISTRATION 等实例级开关还会被「配置缓存」固化(DB + Redis 两级),仅改 .env 重建后可能仍读旧值,清法见 11.6。

八、配置文件挂载

挂载说明
./storage:/var/www/html/storage全部持久化状态(媒体/日志/OAuth 密钥/缓存目录),必须 UID 33 属主
./mariadb-data:/var/lib/mysql数据库数据目录
./redis-data:/dataRedis AOF 数据

storage 目录内首次部署需具备发行包骨架(应用启动报 cache path 错误即缺失,见 11.2);由 AUTORUN 的「Setting Laravel storage permissions」步骤自动补齐,或在部署前手工创建:app/{backups,bouncer,nlp,remcache}、framework/{cache/data,sessions,testing,views}、logs、debugbar、purify。

九、等价 docker run(理解编排等价形态)

将本编排的应用服务单独跑起来的等价命令(用于理解映射关系,实际部署仍以 compose 为准):

# 数据库
docker run -d --name pixelfed-0.15.2-db \
  -e MYSQL_DATABASE=pixelfed -e MYSQL_USER=pixelfed -e MYSQL_PASSWORD=q1w2e3r4 \
  -e MYSQL_ROOT_PASSWORD=q1w2e3r4 \
  -v /opt/pixelfed/mariadb-data:/var/lib/mysql mariadb:11.8

# 应用(含端口映射与存储挂载)
docker run -d --name pixelfed-0.15.2-app \
  --env-file /opt/pixelfed/.env \
  -p 8135:8080 \
  -e AUTORUN_ENABLED=true -e AUTORUN_LARAVEL_MIGRATION=true \
  -v /opt/pixelfed/storage:/var/www/html/storage \
  ghcr.io/pixelfed/pixelfed:v0.15.2

# 队列工作器
docker run -d --name pixelfed-0.15.2-horizon \
  --env-file /opt/pixelfed/.env \
  -v /opt/pixelfed/storage:/var/www/html/storage \
  ghcr.io/pixelfed/pixelfed:v0.15.2 php artisan horizon

# 定时任务调度器
docker run -d --name pixelfed-0.15.2-scheduler \
  --env-file /opt/pixelfed/.env \
  -v /opt/pixelfed/storage:/var/www/html/storage \
  ghcr.io/pixelfed/pixelfed:v0.15.2 php artisan schedule:work

# 缓存
docker run -d --name pixelfed-0.15.2-redis \
  -v /opt/pixelfed/redis-data:/data redis:8-alpine redis-server --appendonly yes

手动形态需自行创建内部网络并把五个实例接入同一网络、配好服务发现(编排已自动完成),故生产推荐使用 compose。

十、常用运维命令

# 状态总览
docker compose ps

# 应用日志(含 AUTORUN 初始化序列)
docker compose logs -f pixelfed

# 队列工作器日志(图片优化/联邦投递在此执行)
docker compose logs -f horizon

# 重启单个服务(.env 改动无效,见 11.6 注意事项)
docker compose restart pixelfed

# .env 修改后的正确生效方式:重建
docker compose up -d

# 进入应用执行 artisan(运维命令全集见命令详解篇)
docker compose exec pixelfed php artisan

# 版本升级:改 .env / compose 中的版本号后
docker compose pull && docker compose up -d

# 备份数据库
docker compose exec db sh -c 'mariadb-dump -uroot -p"$MYSQL_ROOT_PASSWORD" pixelfed' > pixelfed-$(date +%F).sql

升级说明:PixelFed 小版本升级(如 0.15.x 内)通常只需替换发行包版本号并 up -d,AUTORUN 会自动执行增量迁移;跨大版本(0.14 → 0.15)请先阅读官方 Release Notes 中的迁移说明并备份后再操作。

十一、常见问题(FAQ)

以下均为本环境实测遇到并解决的故障,按触发顺序排列。

Q1:应用启动后一直重启,日志报 storage/logs 权限拒绝

现象(日志实测):

file_put_contents(/var/www/html/storage/logs/laravel.log): Failed to open stream: Permission denied

原因:宿主 ./storage 由 root 创建,应用发行包内运行用户为 www-data(UID 33)无写权限。 解决:

chown -R 33:33 /opt/pixelfed/storage
docker compose up -d

Q2:权限修复后仍重启,报 Please provide a valid cache path

实测回显:

Please provide a valid cache path.

原因:storage 目录是空挂载,缺少 Laravel 必需的骨架子目录(framework/views 等)。 解决:补齐骨架目录并保持 33 属主(步骤见 4.1 与第八章骨架清单),或先启动一次让 AUTORUN 的权限步骤创建。

Q3:数据库迁移失败:Expression of generated column 'key_hash' contains a disallowed function: md5

实测报错(在 MySQL 9 上直接复现该建表语句):

ERROR 3763 (HY000) at line 1: Expression of generated column 'key_hash' contains a disallowed function: `md5`.

同一语句在 MariaDB 11.8 实测执行成功(建表返回 Query OK,表创建成功)。原因:MySQL 9 禁止生成列使用 md5(),而 Laravel Pulse 迁移定义了 key_hash char(16) ... AS (unhex(md5(\key`)))。 解决:database 服务改用 mariadb:11.8(本文方案),并删除 MySQL 9 产生的旧数据目录后重新 docker compose up -d`。改后全量迁移实测 268 个全部 Ran。

Q4:内网 HTTP 部署,登录表单提交后回到登录页,永远登不上

两个叠加原因,均有实测证据:

  1. 表单/链接被强制生成为 https(.env 未显式关 FORCE_HTTPS_URLS)。实测登录页表单 action:
    <form method="POST" action="https://192.168.146.128:8135/login">
    
    关闭后变为 http。
  2. 会话 Cookie 带 secure 标志(SESSION_SECURE_COOKIE 默认 true),HTTP 请求下浏览器不回传 Cookie。实测响应头:
    set-cookie: pxfs=...; path=/; httponly; samesite=lax; secure
    

解决:.env 增加 FORCE_HTTPS_URLS="false" 与 SESSION_SECURE_COOKIE="false",然后 docker compose up -d 重建(restart 不会重新读取 .env,这是另一个易错点)。

Q5:全站 404,但 API 正常

现象:curl http://127.0.0.1:8135/login 返回 404,而 /api/v1/instance 正常返回 200。 原因:路由按 APP_DOMAIN 注册了域名约束,用 127.0.0.1/其他地址访问不匹配任何路由。 解决:始终用与 APP_URL/APP_DOMAIN 一致的地址访问(本文即 http://192.168.146.128:8135),实测该地址下 /login 返回 200。

Q6:改了 STORIES_ENABLED / OPEN_REGISTRATION 后行为不变

原因三层叠加(实测闭环):

  1. docker compose restart 不重读 .env,需 up -d 重建;
  2. 实例级开关会被「配置缓存」固化在数据库 config_caches 表 + Redis(config_cache:_v0-key:... 键),首启时种入旧值;
  3. 路由缓存编译时读的也是该缓存值(/register 路由按开关条件注册)。 解决:清对应键与库表行后重建缓存,实测闭环:OPEN_REGISTRATION=false → /register 404;置 true 并清缓存 → /register 200;还原后恢复 404。具体命令:
docker compose exec pixelfed php artisan tinker --execute='$r=\App\Models\ConfigCache::where("k","pixelfed.open_registration")->first(); if($r)$r->delete(); \Illuminate\Support\Facades\Cache::forget("config_cache:_v0-key:pixelfed.open_registration");'
docker compose exec pixelfed sh -c 'php artisan optimize:clear && php artisan optimize'

Q7:健康检查失败:healthcheck-frankenphp / mysqladmin 不存在

实测报错:

OCI runtime exec failed: exec: "healthcheck-frankenphp": executable file not found in $PATH: unknown
OCI runtime exec failed: exec: "mysqladmin": executable file not found in $PATH: unknown

原因:官方编排引用的健康检查脚本/命令与 v0.15.2 发行包不匹配(发行包内置的是 healthcheck-horizon/healthcheck-schedule/healthcheck-octane 等;MariaDB 11.8 管理命令为 mariadb-admin)。 解决:按第四章编排文件修正——应用用 API 探活(curl -fsS http://127.0.0.1:8080/api/v1/instance),数据库用 mariadb-admin ping。

Q8:import:cities 报 storage/app/cities.json 不存在

实测回显:

Error: The file storage/app/cities.json is missing. Run `git checkout storage/app/cities.json` to download it.

原因:发行包不含该数据文件(源码仓库才带)。 解决:从 GitHub 对应 tag 下载 cities.json 放入存储目录(实测 12977722 字节):

curl -fsSL -o /opt/pixelfed/storage/app/cities.json \
  https://raw.githubusercontent.com/pixelfed/pixelfed/v0.15.2/storage/app/cities.json
chown 33:33 /opt/pixelfed/storage/app/cities.json
docker compose exec pixelfed php artisan import:cities

十二、参考链接