Ubuntu 22.04 安装 phpList 3.6.17 指南

在 Ubuntu 22.04 上以 LAMP 方式手工安装 phpList 3.6.17:源码包部署、数据库准备、config.php 配置、初始化向导、SMTP+CLI 发信链路验证与发送失败排障实录。

phpListphpList2026/10/26

Ubuntu 22.04 安装 phpList 3.6.17 指南

目标:在一台全新 Ubuntu 22.04 服务器上,以 LAMP(Apache + PHP + MariaDB)方式手工安装 phpList 3.6.17:完成官方源码包下载部署、数据库准备、config.php 最小配置、Apache 站点配置、Web 初始化向导与管理员账号创建,并打通「SMTP 投递 + 命令行发送 + 队列处理」的完整发信链路,最后沉淀本次实测中踩到的发送失败排障实录。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(/etc/os-release 实测回显 PRETTY_NAME="Ubuntu 22.04.5 LTS")
  • phpList 版本:3.6.17(官方最新稳定版;GitHub Releases 的 releases/latest 于 2026-10-26 查询时重定向至 v3.6.17。注意:SourceForge 上已出现 3.7.0 目录,但对应的是 3.7.0-RC5 预发布线(发布于 2026-08-10,与 GitHub v3.7.0-RC5 同期),GitHub 上不存在 v3.7.0 正式 tag,生产安装请认准 3.6.17)
  • 组件版本(安装后 dpkg -l 实测回显):
组件版本
apache22.4.52-1ubuntu4.25
libapache2-mod-php2:8.1+92ubuntu1
php2:8.1+92ubuntu1
php8.1-mysql / xml / mbstring / imap / curl / gd / zip8.1.2-1ubuntu2.28
mariadb-server1:10.6.23-0ubuntu0.22.04.1
  • 网络端口规划:Web 服务监听 8130(示例值,避免与已有站点冲突;生产可直接用 80),MariaDB 3306,SMTP 发送测试捕获器 1025
  • 演示账号:管理员 admin / q1w2e3r4,数据库账号 phplist / q1w2e3r4
  • 下载渠道说明:官方 SourceForge 直连实测可用(⚠️ 注意 SourceForge 的 3.7.0 目录是 RC 预发布,稳定版请使用 3.6.17 路径)

二、安装前准备

2.1 更新系统并安装依赖

phpList 3.6.17 是标准 PHP 应用,依赖 Web 服务器、PHP 及其扩展、MariaDB。执行:

apt-get update
apt-get install -y apache2 mariadb-server php libapache2-mod-php \
  php8.1-mysql php8.1-xml php8.1-mbstring php8.1-imap \
  php8.1-curl php8.1-gd php8.1-zip

安装完成后核对版本(实测回显节选):

php -v | head -1
PHP 8.1.2-1ubuntu2.28 (cli) (built: Oct 19 2026 18:54:45) (NTS)
apache2 -v | head -1
mysql --version
Server version: Apache/2.4.52 (Ubuntu)
mysql  Ver 15.1 Distrib 10.6.23-MariaDB, for debian-linux-gnu (x86_64) using  EditLine wrapper

确认 PHP 模块已被 Apache 加载(实测回显):

apachectl -M 2>/dev/null | grep php
php_module (shared)

2.2 启动并验证基础服务

在标准 Ubuntu 服务器上:

systemctl enable --now apache2 mariadb

验证监听端口:

ss -ltn | grep -E ':(80|8130|3306)'

实测回显(本例 Apache 配置为监听 8130,见 4.2 节;MariaDB 默认监听 3306):

LISTEN 0      80         127.0.0.1:3306       0.0.0.0:*
LISTEN 0      511          0.0.0.0:8130       0.0.0.0:*

⚠️ 提示:若服务器未启用 systemd(如最小化安装环境),可用 service apache2 start 与 service mariadb start 替代,service apache2 status 实测回显 * apache2 is running。

2.3 创建数据库与账号

phpList 安装向导只会建表,不建库。先手工创建数据库与专用账号:

mysql -e "CREATE DATABASE phplistdb CHARACTER SET utf8mb4;"
mysql -e "CREATE USER 'phplist'@'localhost' IDENTIFIED BY 'q1w2e3r4';"
mysql -e "GRANT ALL PRIVILEGES ON phplistdb.* TO 'phplist'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"

以上语句执行成功时无输出。验证(实测回显):

mysql -e "show databases"
Database
information_schema
mysql
performance_schema
phplistdb
sys
mysql -e "show grants for 'phplist'@'localhost'"
Grants for phplist@localhost
GRANT USAGE ON *.* TO `phplist`@`localhost` IDENTIFIED BY PASSWORD '*0971389F8E7F1AEB999104BDA7A0FA145087F348'
GRANT ALL PRIVILEGES ON `phplistdb`.* TO `phplist`@`localhost`

三、安装 phpList 3.6.17

3.1 下载官方源码包

从 SourceForge 官方项目页下载 3.6.17 稳定版(实测直连可用,约 23 MB,耗时 7.2 秒):

cd /root
wget -O phplist-3.6.17.tgz "https://sourceforge.net/projects/phplist/files/phplist/3.6.17/phplist-3.6.17.tgz/download"

实测回显(节选尾部):

 22700K .......... .......... .......... .......... .......... 99% 21.9M 0s
 22750K .......... .......... .......... .......... .......... 99% 34.2M 0s
 22800K .......... ........                                   100% 53.8K=7.2s

2026-10-26 10:50:01 (3.09 MB/s) - 'phplist-3.6.17.tgz' saved [23365673/23365673]

⚠️ 注意:SourceForge 的 files/phplist/3.7.0/ 目录目前放置的是 3.7.0 预发布(RC5)产物,稳定版请认准 3.6.17 路径。官方 GitHub Releases(github.com/phpList/phpList3)不提供 tgz 附件,源码包以 SourceForge 为准。

3.2 校验与解压

校验文件完整性(实测回显):

ls -la phplist-3.6.17.tgz
sha256sum phplist-3.6.17.tgz
-rw-r--r-- 1 root root 23365673 Aug  9 15:30 phplist-3.6.17.tgz
bcc790fd451862f03f2a0476d8ce9e8a794211d946613e0f7415dd4cde08e3d8  phplist-3.6.17.tgz

查看包内顶层结构(实测回显):

tar -tzf phplist-3.6.17.tgz | head -3
phplist-3.6.17/
phplist-3.6.17/public_html/
phplist-3.6.17/public_html/index.html

解压:

tar -xzf phplist-3.6.17.tgz

3.3 部署到 Web 目录并设置权限

将包内 public_html 目录部署为 /var/www/lists(phpList 官方推荐的应用路径名):

mv /root/phplist-3.6.17/public_html /var/www/lists
chown -R www-data:www-data /var/www/lists
ls /var/www/lists

实测回显:

admin
api.php
base
config
dl.php
images
index.html
index.php
js
lt.php
styles
texts
updater
ut.php

目录说明:admin/ 是管理后台,config/ 存放配置文件,其余为订阅前台与静态资源。

四、配置 phpList 与 Apache

4.1 编写最小配置文件 config.php

phpList 的配置文件是 config/config.php(发布包中为精简模板)。编辑 /var/www/lists/config/config.php,核心改动如下(与实测生效文件一致):

// 数据库连接(对应 2.3 节创建的库与账号)
$database_host = 'localhost';
$database_name = 'phplistdb';
$database_user = 'phplist';
$database_password = 'q1w2e3r4';

// SMTP 发送服务器。留空则使用 PHP mail() 函数;
// 本例指向本机 1025 端口的测试捕获器(见 6.1 节),生产环境替换为真实 SMTP 服务器
define('PHPMAILERHOST', '127.0.0.1');
define('PHPMAILERPORT', 1025);

// 必须改为 0 才会真实发送;默认值为 1(仅模拟发送,不投递)
define('TEST', 0);

⚠️ 发布包默认带 define('TEST', 1);,官方注释明确说明这是防止「未确认配置就意外发信」的保护开关。忘记改成 0 时,队列会走模拟流程、邮件不会真实投出,这是新装环境最常踩的坑之一。

如需退信处理与更多高级参数,可参考同目录下 900 余行的 config_extended.php,将所需段落复制进 config.php(参数详解见本专栏《phpList 3.6.17 配置参数详解》)。

4.2 配置 Apache 站点(端口 8130)

本例让 Apache 监听 8130 并以 /var/www 为站点根,使应用通过 http://<服务器IP>:8130/lists/ 访问。

修改监听端口(/etc/apache2/ports.conf):

sed -i 's/^Listen 80/Listen 8130/' /etc/apache2/ports.conf

修改默认站点(/etc/apache2/sites-enabled/000-default.conf)两处:

<VirtualHost *:8130>
        DocumentRoot /var/www

(生产环境若使用 80 端口,保持 Listen 80 与 <VirtualHost *:80> 即可,其余步骤相同。)

4.3 重启并验证 Web 服务

apachectl configtest

实测回显:

Syntax OK
apachectl restart
ss -ltn | grep 8130

实测回显:

LISTEN 0      511          0.0.0.0:8130       0.0.0.0:*

验证管理后台可访问(实测回显):

curl -sI http://127.0.0.1:8130/lists/admin/ | head -2
HTTP/1.1 200 OK
Date: Mon, 26 Oct 2026 10:47:11 GMT

五、初始化向导与管理员账号

5.1 访问初始化向导

首次访问 http://127.0.0.1:8130/lists/admin/,phpList 会自动跳转到数据库初始化向导(页面标题 phpList :: phpList :: Initialise the database,页面 footer 标识 Powered-By: phplist version 3.6.17)。

向导页面提供「创建管理员」表单,字段包括:管理员登录名(adminname)、组织名称(orgname)、管理员密码(adminpassword)、管理员邮箱(adminemail)。

⚠️ 排障提示:若数据库此前已被初始化过(例如向导被重复访问、或曾用命令行 -pinitialise 初始化过),再次访问向导会出现 Database error 1050 ... Table 'phplist_xxx' already exists ... failed 的建表报错,并提供「Upgrade instead?」与「Force Initialisation (will erase all data!)」两个选项。此时不要点 Force(会清空数据),直接登录即可;全新库则不会出现该现象。

5.2 提交初始化并创建管理员

在向导表单中填入:

  • 管理员登录名:admin
  • 组织名称:example
  • 管理员密码:q1w2e3r4
  • 管理员邮箱:admin@example.com

提交后系统自动完成建表(60 余张 phplist_* 表)、写入默认系统配置,并创建管理员账号。初始化成功后页面转入登录入口。

5.3 登录后台验证

提交管理员凭证登录(实测回显):

curl -s -c /root/b1ck.txt -d "login=admin&password=q1w2e3r4" \
  "http://127.0.0.1:8130/lists/admin/?page=login" \
  -o /root/login_resp.html -w "login POST http_code: %{http_code}\n"
login POST http_code: 200

携带会话 Cookie 访问后台首页(实测回显):

curl -s -b /root/b1ck.txt "http://127.0.0.1:8130/lists/admin/?page=main" | grep -oE "<title>[^<]*</title>"
<title>phpList :: phpList :: main</title>

登录成功,浏览器访问 http://<服务器IP>:8130/lists/admin/ 即可进入管理后台(账号 admin / q1w2e3r4)。

六、发信链路验证(SMTP + CLI)

以下步骤在服务器本机完成,验证「创建活动 → 队列处理 → SMTP 投递」链路。

6.1 启动本地 SMTP 捕获器

生产环境中 PHPMAILERHOST 指向真实邮件服务器;本机验证可用 Python 自带的 aiosmtpd 起一个只收不发、把邮件打印到终端的调试 SMTP 服务:

apt-get install -y python3-aiosmtpd
python3 -u -m aiosmtpd -n -l 127.0.0.1:1025 > /root/smtpd.log 2>&1 &

验证端口就绪(实测回显):

python3 -c "import socket;s=socket.create_connection(('127.0.0.1',1025),3);print(s.recv(64).decode().strip())"
220 mail.example.com Python SMTP 1.4.2

6.2 安装命令行包装脚本

phpList 的命令行入口是 admin/index.php,官方发布包自带 bin/phplist 包装脚本。为便于使用,创建 /usr/local/bin/phplist(内容与实测生效脚本一致):

cat > /usr/local/bin/phplist <<'EOF'
#!/bin/bash
# phpList 命令行入口:用户参数在前,-c 指定配置文件在后
/usr/bin/php /var/www/lists/admin/index.php $* -c /var/www/lists/config/config.php
EOF
chmod +x /usr/local/bin/phplist

验证版本(实测回显):

phplist --version
phpList - phpList version 3.6.17 (c) 2000-2026 phpList Ltd, https://www.phplist.com

⚠️ 实测注意:包装脚本中 $*(用户参数)必须放在 -c 之前。phpList 的命令行参数解析会把 key=value 形式的参数写入请求上下文,若 -c 在前,部分子命令的参数会被吞掉,出现「Cannot find config file」之类的误导性报错。

命令行默认以 root 运行即可;若在 config.php 中配置了 $commandline_users 白名单,则仅白名单内的系统用户可运行(详见《phpList 3.6.17 常用 CLI 命令详解》)。

6.3 准备列表与订阅者,创建测试发送

通过 Web 后台(http://<服务器IP>:8130/lists/admin/,账号 admin / q1w2e3r4)完成两项准备:

  1. 进入 Lists 页面,创建列表 demo-list;
  2. 进入 Users 页面,添加测试订阅者 user1@example.com、user3@example.com(保持 confirmed 状态),并加入 demo-list。

编写测试正文并创建发送活动(-l 指定列表名,-s 指定主题,-f 指定发件人地址,正文从标准输入读入):

printf "Hello team,\n\nThis is a CLI delivery test from phpList 3.6.17.\n" > /root/test-body.txt
phplist -psend -s FromFlagCheck -l demo-list -f admin@example.com < /root/test-body.txt

实测回显:

phpList - phpList version 3.6.17 (c) 2000-2026 phpList Ltd, https://www.phplist.com
Message with subject FromFlagCheck was sent to "demo-list" 

⚠️ 实测注意:-f 必须紧跟一个完整词(如 admin@example.com),且主题等参数值中避免再出现 -x 形式的词——phpList 会按空格重新分词解析参数,拆散的参数会被错误归属(本系列实测中 -s "From via -f flag" 就曾导致主题被截成 From via、flag 被 -f 吞掉)。

6.4 处理队列并验证收信

处理发送队列(实测回显):

phplist -pprocessqueue
phpList - phpList version 3.6.17 (c) 2000-2026 phpList Ltd, https://www.phplist.com
phpList - Recently sent : 0
phpList - Started [0.0007750000] (123)
phpList - Sending in batches of 1,000 emails [0.0000490000] (124)
phpList - Processing has started, [0.0001540000] (127)
phpList - One campaign to process. [0.0002800000] (129)
phpList - Processing campaign 7 [0.0012610000] (146)
phpList - Looking for subscribers [0.0005420000] (151)
phpList - Found them: 2 to process [0.0005740000] (157)
phpList - sent 1 ETA Mon 26 Oct 10:44 sending 59388 msg/hrphpList - sent 2 ETA Mon 26 Oct 10:44 sending 66503 msg/hrphpList - Processed 2 out of 2 subscribers [0.1029420000] (223)
phpList - Script stage: 5 [0.0020420000] (236)
phpList - 2 messages sent in 0.11 seconds (64524 msgs/hr) [0.0002160000] (240)
phpList - Finished this run [0.0008410000] (245)

查看 SMTP 捕获器收到的邮件(实测回显,/root/smtpd.log):

---------- MESSAGE FOLLOWS ----------
Date: Mon, 26 Oct 2026 10:44:00 +0000
To: user1@example.com
From: "admin@example.com" <admin@example.com>
Subject: FromFlagCheck
Message-ID: <xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@localhost.localdomain>
X-phpList-version: 3.6.17
...
------------ END MESSAGE ------------

邮件真实投出、From 头正确,发送链路验证完成。

七、排障实录

7.1 全部发送失败:Invalid address (Sender)

现象:phplist -pprocessqueue 日志出现 2 failed (will retry later)、2 sendemail returned false、2 failed_sent,且伴随 Warning: script never reached stage 5,SMTP 捕获器一无所获。

定位:processqueue 的标准输出不给出根因,需查数据库事件日志:

mysql phplistdb -e "select entered,left(entry,130) from phplist_eventlog order by id desc limit 6"

实测回显:

entered             left(entry,130)
2026-10-26 10:41:04  Warning: script never reached stage 5\nThis may be caused by a too slow or too busy server \n
2026-10-26 10:41:04  Finished this run
2026-10-26 10:41:04  2 failed (will retry later)
2026-10-26 10:41:04  Processed 2 out of 2 subscribers
2026-10-26 10:41:04  Error sending email to user3@example.com Invalid address:  (Sender): webmaster@127.0.0.1:8130
2026-10-26 10:41:04  Error sending email to user1@example.com Invalid address:  (Sender): webmaster@127.0.0.1:8130

根因:phpList 初始化时会把若干默认值写入数据库配置表(phplist_config),其中发件人默认值为 noreply@[DOMAIN],[DOMAIN] 占位符取自初始化时的访问域名。本例初始化时用了带端口的 127.0.0.1:8130,于是默认发件人被拼成 noreply@127.0.0.1:8130——这是非法邮箱地址;退信信封(Sender)同样回退到 webmaster@127.0.0.1:8130,PHPMailer 地址校验直接失败,整封邮件发送中断(实测回显数据库配置值):

item                  value
domain                127.0.0.1:8130
message_from_address  noreply@[DOMAIN]
message_from_name     Webmaster

解决:

  1. 保证 config.php 中配置了退信信封地址 $message_envelope(合法邮箱即可,例如 listbounces@example.com),这是 Sender 的可靠来源,缺失时才会回退到非法默认值;
  2. 命令行发送时用 -f 显式指定发件人(见 6.3 节),或在后台「设置」中把活动默认发件人(campaignfrom_default)改写为合法地址;Web 向导正常初始化时会把管理员邮箱自动写入该默认值。

验证:补齐配置后重跑 phplist -pprocessqueue,队列自动重试上次标记 failed_sent 的订阅者,实测回显 2 messages sent in 0.11 seconds (66174 msgs/hr),SMTP 捕获器收到完整邮件,From 头正常。

7.2 从头为空的 From

现象:邮件能投出,但收到的邮件 From: 头为空(实测捕获到 From: 后无任何内容),此类邮件在真实邮件服务器上大概率被拒收或进垃圾箱。

根因:From 头来自活动自身的发件人字段(fromfield)。命令行 -psend 不带 -f 时,fromfield 由数据库配置 message_from_name + message_from_address 拼接,本例拼出 Webmaster noreply@127.0.0.1:8130——地址部分非法,phpList 将其置空后仍然投递,产生无效邮件。

解决:见 7.1 的第 2 条;带 -f admin@example.com 重发后,实测 From 头为 "admin@example.com" <admin@example.com>,问题消失。

7.3 重复初始化:Table already exists

现象与处理:见 5.1 节排障提示——数据库已初始化时向导会报 Database error 1050 ... already exists,此时直接登录后台即可,切勿点「Force Initialisation」清空数据。

八、常见问题与后续文档

Q1:队列跑完显示 sent 但收不到邮件? 按顺序检查:① config.php 是否仍是默认 define('TEST', 1)(模拟发送,不真实投递);② PHPMAILERHOST/PHPMAILERPORT 是否指向可达的 SMTP 服务(端口写错会直接连接失败);③ 查 phplist_eventlog 表定位真实错误(参见 7.1 节)。

Q2:向导页面空白或报 PHP 函数缺失? 多为 PHP 扩展不全。本例实测所需扩展集合为:mysql、xml、mbstring、imap、curl、gd、zip(对应 php8.1-* 包),逐项核对 dpkg -l | grep php8.1。

Q3:如何实现定时无人值守发送? 将 processqueue、processbounces 挂入 crontab,并把 MANUALLY_PROCESS_QUEUE 设为 0 禁止后台手动触发,详见本专栏《phpList 3.6.17 常用 CLI 命令详解》。

Q4:安装方式如何选择? 手工 LAMP 安装适合已有 PHP 运行环境的服务器;容器化环境可直接使用官方镜像编排部署,见《phpList 3.6.17 Docker Compose 部署指南》。产品定位与选型背景可先阅读 《phpList 3.6.17 介绍与选型指南》。