部署轻量化博客 CMS - Typecho
部署轻量化博客 CMS - Typecho
一、前言
1.1 适用环境
本教程基于轻量应用服务器环境编写,适用于 2 核 2GB 内存 40GB 磁盘及以上的服务器。操作系统以 Ubuntu 22.04 / 24.04 为例,大部分命令在 CentOS / RedHat 上也可通用(仅包管理命令不同:Ubuntu / Debian 用 apt,CentOS / RedHat 用 yum / dnf)。
1.2 为什么用 Docker Compose
相比传统在服务器内部直接安装 LNMP(Linux + Nginx + MySQL + PHP),Docker Compose 有以下优势:
- 环境隔离:所有服务运行在独立容器中,互不干扰,不污染宿主机系统环境。
- 一键部署:一条
docker compose up -d即可完成整个栈的启动,迁移或重建成本极低。 - 数据持久化:通过 bind mount 将容器数据挂载到宿主机目录,容器删除重建后数据依然存在。
- 版本可控:每个服务使用指定镜像版本,升级回滚简单透明,不会出现依赖冲突。
- 资源可控:可为每个容器单独设置 CPU 和内存上限,防止某个服务吃满资源导致整机崩溃。
1.3 最终架构概览
用户浏览器 → Nginx(端口 80) → PHP-FPM(Typecho 应用) → MariaDB(数据库)三个容器通过自定义网络 typecho-net 互通,数据通过 bind mount 持久化到宿主机 /opt/cms-server/typecho/ 目录下。
二、准备工作
2.1 购买服务器、记录公网 IP 和账号密码
在云服务商(如阿里云、腾讯云等)购买云服务器实例后,请务必记录好:
- 公网 IP 地址
- 登录用户名(通常为
root或ubuntu) - 登录密码
这些信息在后续通过 SFTP / SSH 连接服务器时会用到。
2.2 SFTP 工具连接(MobaXterm)
本教程使用 MobaXterm 个人版(免费)。相比云服务商自带的 Web 终端(如阿里云 Workbench),第三方 SFTP 工具在文件上传、命令执行上更方便。
连接步骤:
- 打开 MobaXterm,左上角点击 Session → SSH
- 在 Remote host 中输入公网 IP
- 点击 OK,提示
Login时输入用户名,提示Password时输入密码 - 连接成功后进入 Command 模式(命令终端)
2.3 确认系统版本
cat /etc/os-release根据输出确认系统类型:
- CentOS / RedHat:包管理用
yum或dnf - Ubuntu / Debian:包管理用
apt
2.4 关于 sudo 权限
登录终端后默认是普通用户。执行系统层面的命令时需要在前面加 sudo,输入后会提示输入密码以获取临时管理员权限。涉及系统文件编辑时,如果没有 sudo,通常会提示"该文件只能读,不可写入"。
提示:虽然可以执行sudo -i直接进入超级管理员模式,但不建议初学者这样做。养成在系统级命令前加sudo的习惯更安全。
三、安装 Docker 与 Compose
3.1 安装 Docker
在命令行执行(国内加速脚本):
bash <(wget -qO- https://get.xuanyuan.cloud/docker.sh)备用地址:
bash <(wget -qO- https://get.xuanyuan.me/docker.sh)安全提示:上述为国内第三方一键安装脚本,执行前建议查看脚本内容。如需官方安装方式,请参考:https://docs.docker.com/engine/install/ubuntu/
执行后提示"请选择操作模式",输入 1 进行安装。等待约 5~10 分钟。安装完成后选择 1(免费版)继续。
3.2 验证安装
docker -v && docker compose version显示版本号即安装成功。
3.3 配置镜像加速器
免费版 Docker 源有时不太稳定,建议添加多个镜像源:
sudo tee /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://docker.xuanyuan.me",
"https://docker.1ms.run",
"https://docker.m.daocloud.io"
]
}
EOF重启 Docker 使配置生效:
sudo systemctl daemon-reload
sudo systemctl restart docker验证:
sudo docker pull hello-world拉取成功即配置正确。若提示节点繁忙,可更换其他免费源,或考虑购买付费源。
四、部署 Typecho
4.1 创建目录结构
建议部署在 /opt 目录下——Linux 惯例中专门放第三方 / 额外安装软件的目录,权限清晰、备份迁移干净、不会被系统更新误伤。
sudo mkdir -p /opt/cms-server/typecho && cd /opt/cms-server/typecho
pwd # 确认当前目录为 /opt/cms-server/typecho目录结构如下:
/opt/cms-server/typecho/
├── docker-compose.yml
├── .env # 密码等敏感配置
├── nginx/
│ ├── conf.d/
│ │ └── typecho.conf
│ └── logs/
├── typecho-data/ # Typecho 程序文件(持久化)
└── mariadb-data/ # 数据库数据(持久化)创建必要文件夹:
mkdir -p nginx/conf.d nginx/logs typecho-data mariadb-data4.2 编写 .env 文件
用 .env 管理密码等敏感配置,避免在 compose 文件中硬编码。
vim .env写入以下内容(请替换为自己的强密码):
# /opt/cms-server/typecho/.env
MYSQL_ROOT_PASSWORD=YourStrongRootPwd123
MYSQL_DATABASE=typecho
MYSQL_USER=typecho
MYSQL_PASSWORD=YourStrongTypechoPwd456安全提示:示例密码请替换为自己的强密码,建议包含大小写字母、数字和特殊字符,长度不少于 12 位。
4.3 编写 docker-compose.yml
vim docker-compose.yml写入以下完整内容:
services:
# 1. MariaDB
db:
image: mariadb:10.6
container_name: typecho-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- ./mariadb-data:/var/lib/mysql
networks:
- typecho-net
# 2c2g 内存有限,给数据库限个上限
deploy:
resources:
limits:
memory: 768M
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# 2. Typecho (PHP-FPM)
app:
image: joyqi/typecho:nightly-php7.4-fpm
container_name: typecho-app
restart: always
volumes:
# 挂载 Typecho 程序目录,容器重建数据不丢
- ./typecho-data:/app
networks:
- typecho-net
depends_on:
db:
condition: service_healthy
deploy:
resources:
limits:
memory: 512M
# 3. Nginx
web:
image: nginx:alpine
container_name: typecho-web
restart: always
ports:
- "80:80"
volumes:
# Nginx 站点配置(只读)
- ./nginx/conf.d:/etc/nginx/conf.d:ro
# Nginx 日志
- ./nginx/logs:/var/log/nginx
# 共享 Typecho 程序目录给 Nginx(关键:让 Nginx 能读到 php 文件)
- ./typecho-data:/app:ro
networks:
- typecho-net
depends_on:
- app
deploy:
resources:
limits:
memory: 256M
networks:
typecho-net:
driver: bridge配置说明:
- 使用
depends_on+healthcheck确保数据库就绪后 Typecho 才启动(避免首次安装报连不上库) - 使用 bind mount 共享
typecho-data给 Nginx,替代已过时的volumes_from - 用
.env存密码,compose 文件中用${VAR}引用,避免密码硬编码 - 给三个服务限制内存,防止 2C2G 服务器被吃满触发 OOM
- 挂载
typecho-data和mariadb-data到宿主机,容器删除 / 重建后数据不丢失
4.4 编写 Nginx 配置
cd /opt/cms-server/typecho/nginx/conf.d
vim typecho.conf写入以下完整配置:
server {
listen 80;
server_name _; # 没有域名就写 _,有域名替换成你的域名
root /app;
index index.php index.html index.htm;
access_log /var/log/nginx/typecho_access.log;
error_log /var/log/nginx/typecho_error.log;
client_max_body_size 20m; # 允许上传稍大的附件
# Typecho 伪静态
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
}
}
# PHP-FPM 处理
location ~ \.php$ {
fastcgi_pass app:9000; # 用服务名 app 连接 PHP-FPM
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
}
# 禁止访问敏感文件
location ~ /\.(?!well-known).* {
deny all;
}
}4.5 启动服务
cd /opt/cms-server/typecho
docker compose up -d4.6 验证部署
# 查看容器状态,确认三个容器都处于 running / healthy
docker compose ps
# 查看日志排查问题
docker compose logs -f app
docker compose logs -f web
docker compose logs -f db如果 Typecho 页面提示无法写入 config.inc.php,调整目录权限:
# PHP-FPM 容器里通常是 www-data (UID 33),把 typecho-data 属主改成它
chown -R 33:33 typecho-data提示:joyqi/typecho镜像里 PHP-FPM 运行用户一般是www-data,对应宿主机 UID 33。如果你用其他 Typecho 镜像,先执行docker compose exec app id确认实际 UID 再修改。
五、初始化 Typecho
5.1 放行安全组端口
在云服务商控制台的安全组规则中放行 80 端口(HTTP) 和 443 端口(HTTPS,后续配置证书需要)。
5.2 访问安装向导
浏览器访问:
http://你的服务器IP进入 Typecho 安装向导页面。
5.3 填写数据库信息
| 字段 | 填写内容 | 对应 .env 变量 |
|---|---|---|
| 数据库地址 | db(服务名,不是 localhost) | — |
| 数据库端口 | 3306 | — |
| 数据库用户名 | typecho | ${MYSQL_USER} |
| 数据库密码 | 你自己设置的密码 | ${MYSQL_PASSWORD} |
| 数据库名 | typecho | ${MYSQL_DATABASE} |
| 表前缀 | typecho_(默认即可) | — |
重要:数据库地址必须填db(Docker Compose 中定义的服务名),不能填localhost或127.0.0.1,否则容器之间无法通信。
5.4 设置后台管理员账号
填写站点名称、管理员用户名、密码和邮箱,完成安装。安装完成后即可使用管理员账号登录 Typecho 后台。
5.5 验证伪静态
安装完成后,访问任意文章页面(非首页),确认伪静态是否生效。如果提示 404,请检查 Nginx 配置中的伪静态规则是否正确写入,以及 Nginx 容器是否正常重启。
六、进阶配置(可选)
6.1 绑定域名
将 server_name 从 _ 替换为你的域名:
server_name your-domain.com www.your-domain.com;然后在云服务商的 DNS 解析中,将域名 A 记录指向服务器公网 IP。
6.2 申请 Let's Encrypt 免费证书
推荐使用 Certbot:
sudo apt update
sudo apt install certbot -y申请前先停掉占用 80 端口的 Nginx 容器:
docker compose stop web
sudo certbot certonly --standalone -d your-domain.com
docker compose start web注意:--standalone 会临时占用 80 端口,与 Nginx 容器冲突,所以申请期间需要先停 web 容器。6.3 配置 HTTPS
申请到证书后,修改 typecho.conf:
server {
listen 443 ssl;
http2 on;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# ... 其余配置同 80 端口 ...
}
# 强制 HTTP 跳转 HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}注意:Nginx 容器默认读不到宿主机的
/etc/letsencrypt目录,需要在docker-compose.yml的web服务里加一行挂载:volumes: - /etc/letsencrypt:/etc/letsencrypt:ro
重启 Nginx 容器:
docker compose restart web七、日常运维
7.1 查看日志与重启服务
# 实时查看 app 容器日志
docker compose logs -f app
# 实时查看 web 容器日志
docker compose logs -f web
# 重启某个服务
docker compose restart app7.2 更新镜像并重建
# 拉取最新镜像
docker compose pull
# 根据新镜像重建容器
docker compose up -d7.3 停止与清理
# 停止所有服务(保留数据和容器)
docker compose down
# 停止并删除容器(保留数据卷)
docker compose down --remove-orphans
# 停止并删除数据卷(危险操作,会清除所有数据!)
docker compose down -v7.4 备份与恢复
备份数据库:
# 确保备份目录存在
mkdir -p /opt/backups
# 读取 .env 变量
cd /opt/cms-server/typecho
set -a; source .env; set +a
# 备份数据库(-T 参数避免终端交互)
docker compose exec -T db \
mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" \
> /opt/backups/typecho-db-$(date +%F).sql备份数据目录:
tar -czf /opt/backups/typecho-data-$(date +%F).tar.gz \
/opt/cms-server/typecho/typecho-data \
/opt/cms-server/typecho/mariadb-data恢复数据库:
# 先停止服务
docker compose down
# 导入备份(把文件名替换为你的备份文件)
docker compose exec -T db mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" \
< /opt/backups/typecho-db-2026-01-01.sql
# 重启服务
docker compose up -d7.5 添加 Swap 分区(2C2G 服务器强烈建议)
2C2G 服务器同时运行 MySQL + PHP + Nginx 容易内存紧张,建议添加 2GB Swap 作为兜底:
# 创建 2G 交换文件
sudo fallocate -l 2G /swapfile
# 设置权限
sudo chmod 600 /swapfile
# 格式化为 swap 文件系统
sudo mkswap /swapfile
# 启用 swap
sudo swapon /swapfile
# 写入 fstab 实现开机自动挂载
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab验证:
# 查看内存和 swap 使用情况
free -h
# 查看 swap 设备状态
swapon --show输出中应能看到 swap 行显示 2G 容量且已启用。
八、验收清单
| 检查项 | 验证方法 | 预期结果 |
|---|---|---|
| 容器运行状态 | docker compose ps | 三个容器均为 running 或 healthy |
| 80 端口可访问 | 浏览器访问 http://服务器IP | 显示 Typecho 安装页面 |
| 数据库连接 | 安装向导填写数据库信息后点击安装 | 安装成功,无连接错误 |
| 后台登录 | 访问 http://服务器IP/admin | 可使用管理员账号登录 |
| 伪静态生效 | 访问任意文章页面 | 正常显示,不报 404 |
| Swap 生效 | free -h | 能看到 swap 分区且容量为 2G |
| 备份可用 | 执行备份命令后检查文件 | /opt/backups/ 下有备份文件 |
附录 A:SSH 保活配置(可选)
SSH 连接在空闲一段时间后可能被云服务商或系统自动断开。可配置"心跳保活"机制。
sudo vi /etc/ssh/sshd_config在文件底部插入:
ClientAliveInterval 60
ClientAliveCountMax 3保存退出后重启 SSH 服务:
sudo systemctl restart ssh
# 或者
sudo systemctl restart sshd注意:执行这条命令时,当前 SSH / SFTP 连接可能会断开,这是正常的,重新连接即可。
检查配置语法:
sudo sshd -t没有报错输出即配置生效成功。
附录 B:常见问题
Q1:访问页面提示 403 Forbidden
检查 Nginx 配置中 root 路径是否正确指向 /app,以及 typecho-data 目录权限是否正确。
Q2:Typecho 提示无法连接数据库
确认 .env 文件中的数据库用户名和密码与安装向导中填写的一致;确认数据库服务名填的是 db 而不是 localhost。
Q3:安装向导提示 config.inc.php 无法写入
执行 chown -R 33:33 typecho-data 修改目录属主,然后刷新页面重试。
Q4:容器启动后立刻退出
使用 docker compose logs <容器名> 查看容器日志排查具体错误原因。
Q5:Docker 拉取镜像超时或失败
检查镜像加速器配置是否正确,尝试更换其他镜像源地址。
附录 C:容器内 UID 确认方法
如果你使用的 Typecho 镜像不是 joyqi/typecho,PHP-FPM 的运行用户 UID 可能不同。可通过以下命令确认:
docker compose exec app id输出类似:
uid=33(www-data) gid=33(www-data) groups=33(www-data)其中的 33 就是需要设置的属主 UID。
评论已关闭