Dujiao-Next Docker Compose 部署教程

Dujiao-Next Docker Compose 部署完整记录:PostgreSQL + Redis + Nginx + HTTPS

这篇文章记录一次在 Linux 服务器上部署 Dujiao-Next 的完整过程。

最终方案:

1
2
3
4
5
6
7
前台地址:https://shop.example.com
后台地址:https://admin.example.com
部署目录:/opt/dujiao-next
数据库:PostgreSQL
缓存/队列:Redis
反向代理:Nginx
HTTPS:Certbot + Let's Encrypt

本文采用生产环境更推荐的 PostgreSQL + Redis 方案,而不是 SQLite。所有涉及隐私的信息,比如真实域名、服务器 IP、邮箱、密码,都用占位符替代。


一、准备工作

假设你已经有:

1
2
3
4
5
6
服务器系统:Debian / Ubuntu
服务器 IP:YOUR_SERVER_IP
主域名:example.com
前台域名:shop.example.com
后台域名:admin.example.com
邮箱:[email protected]

先在域名 DNS 里添加两条 A 记录:

1
2
shop    A    YOUR_SERVER_IP
admin A YOUR_SERVER_IP

如果使用 Cloudflare,建议部署和申请证书阶段先关闭代理,也就是使用 仅 DNS / 灰云。等 HTTPS 成功后,再按需开启代理。


二、登录服务器

1
ssh root@YOUR_SERVER_IP

下面默认以 root 用户执行。如果你不是 root 用户,可以在命令前加 sudo


三、修复主机名解析问题,可选但推荐

有些服务器执行 sudo 时会出现:

1
sudo: unable to resolve host your-hostname: Name or service not known

可以这样修复:

1
echo "127.0.1.1 $(hostname)" >> /etc/hosts

检查:

1
2
hostname
cat /etc/hosts

确保里面有类似:

1
127.0.1.1 your-hostname

四、安装基础组件、Docker、Nginx、Certbot

先更新软件源并安装基础依赖:

1
2
apt update
apt install -y ca-certificates curl gnupg openssl python3 python3-yaml nginx certbot python3-certbot-nginx dnsutils

卸载可能冲突的旧 Docker 包:

1
2
3
for pkg in docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc; do
apt remove -y "$pkg" || true
done

添加 Docker 官方仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
. /etc/os-release

if [ "$ID" != "ubuntu" ] && [ "$ID" != "debian" ]; then
echo "当前脚本只适合 Ubuntu/Debian。你的系统是:$ID"
exit 1
fi

CODENAME="${UBUNTU_CODENAME:-$VERSION_CODENAME}"

install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/${ID}/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/${ID} ${CODENAME} stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null

安装 Docker Engine 和 Compose 插件:

1
2
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

启动 Docker:

1
systemctl enable --now docker

检查 Docker:

1
2
3
docker --version
docker compose version
docker run hello-world

五、创建部署目录

1
2
mkdir -p /opt/dujiao-next/{config,data/db,data/uploads,data/logs,data/redis,data/postgres}
cd /opt/dujiao-next

给数据目录写入权限:

1
chmod -R 0777 ./data/logs ./data/db ./data/uploads ./data/redis ./data/postgres

六、下载 Dujiao-Next 配置模板

1
2
3
cd /opt/dujiao-next

curl -L https://raw.githubusercontent.com/dujiao-next/dujiao-next/main/config.yml.example -o ./config/config.yml

七、生成 .env 环境变量文件

这里会自动生成 Redis 密码、PostgreSQL 密码、后台初始密码等信息。

为了写博客,下面使用占位符风格。实际执行时建议保留 openssl rand 自动生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cd /opt/dujiao-next

export APP_SECRET="$(openssl rand -hex 32)"
export JWT_SECRET="$(openssl rand -hex 32)"
export USER_JWT_SECRET="$(openssl rand -hex 32)"
export REDIS_PASSWORD="$(openssl rand -hex 24)"
export POSTGRES_PASSWORD="$(openssl rand -hex 24)"

# 后台初始账号,首次初始化时生效
export ADMIN_USERNAME="admin"
export ADMIN_PASSWORD="Admin$(openssl rand -hex 10)"

cat > .env <<EOF
TAG=latest
TZ=Asia/Shanghai

API_PORT=8080
USER_PORT=8081
ADMIN_PORT=8082

DJ_DEFAULT_ADMIN_USERNAME=${ADMIN_USERNAME}
DJ_DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD}

REDIS_PASSWORD=${REDIS_PASSWORD}

POSTGRES_DB=dujiao_next
POSTGRES_USER=dujiao
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
EOF

查看生成结果:

1
cat /opt/dujiao-next/.env

你会看到类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TAG=latest
TZ=Asia/Shanghai

API_PORT=8080
USER_PORT=8081
ADMIN_PORT=8082

DJ_DEFAULT_ADMIN_USERNAME=admin
DJ_DEFAULT_ADMIN_PASSWORD=Adminxxxxxxxxxxxxxxxxxxxx

REDIS_PASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
POSTGRES_DB=dujiao_next
POSTGRES_USER=dujiao
POSTGRES_PASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

请务必保存后台初始密码,首次登录后马上修改。


八、修改 config.yml

这一步会把 Dujiao-Next 配置为 PostgreSQL + Redis 模式。

先加载 .env

1
2
3
4
5
6
7
8
9
10
11
cd /opt/dujiao-next

set -a
. /opt/dujiao-next/.env
set +a

export APP_SECRET="$(openssl rand -hex 32)"
export JWT_SECRET="$(openssl rand -hex 32)"
export USER_JWT_SECRET="$(openssl rand -hex 32)"
export ADMIN_USERNAME="${DJ_DEFAULT_ADMIN_USERNAME}"
export ADMIN_PASSWORD="${DJ_DEFAULT_ADMIN_PASSWORD}"

然后执行 Python 脚本修改配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
python3 <<'PY'
import os
import yaml
from pathlib import Path

path = Path("/opt/dujiao-next/config/config.yml")
cfg = yaml.safe_load(path.read_text())

cfg.setdefault("app", {})
cfg["app"]["secret_key"] = os.environ["APP_SECRET"]

cfg.setdefault("server", {})
cfg["server"]["mode"] = "release"
cfg.setdefault("server", {}).setdefault("log", {})
cfg["server"]["log"]["dir"] = "/app/logs"

cfg.setdefault("database", {})
cfg["database"]["driver"] = "postgres"
cfg["database"]["dsn"] = (
f"host=postgres user={os.environ['POSTGRES_USER']} "
f"password={os.environ['POSTGRES_PASSWORD']} "
f"dbname={os.environ['POSTGRES_DB']} "
"port=5432 sslmode=disable TimeZone=Asia/Shanghai"
)
cfg["database"]["pool"] = {
"max_open_conns": 25,
"max_idle_conns": 5,
"conn_max_lifetime_seconds": 3600,
"conn_max_idle_time_seconds": 600,
}

cfg.setdefault("jwt", {})
cfg["jwt"]["secret"] = os.environ["JWT_SECRET"]

cfg.setdefault("user_jwt", {})
cfg["user_jwt"]["secret"] = os.environ["USER_JWT_SECRET"]

cfg.setdefault("bootstrap", {})
cfg["bootstrap"]["default_admin_username"] = os.environ["ADMIN_USERNAME"]
cfg["bootstrap"]["default_admin_password"] = os.environ["ADMIN_PASSWORD"]

cfg.setdefault("redis", {})
cfg["redis"].update({
"enabled": True,
"host": "redis",
"port": 6379,
"password": os.environ["REDIS_PASSWORD"],
"db": 0,
"prefix": "dj",
})

cfg.setdefault("queue", {})
cfg["queue"].update({
"enabled": True,
"host": "redis",
"port": 6379,
"password": os.environ["REDIS_PASSWORD"],
"db": 1,
"concurrency": 10,
"queues": {
"default": 10,
"critical": 5,
},
})

path.write_text(yaml.safe_dump(cfg, allow_unicode=True, sort_keys=False))
PY

检查配置是否生效:

1
2
3
grep -n "driver:" /opt/dujiao-next/config/config.yml
grep -n "host=postgres" /opt/dujiao-next/config/config.yml
grep -n "host: redis" /opt/dujiao-next/config/config.yml

正常应该看到类似:

1
2
3
4
driver: postgres
dsn: host=postgres ...
host: redis
host: redis

踩坑:No module named 'yaml'

如果执行 Python 脚本时出现:

1
ModuleNotFoundError: No module named 'yaml'

说明缺少 python3-yaml,重新安装即可:

1
2
3
apt update
apt install -y python3-yaml
python3 -c "import yaml; print('yaml ok')"

九、创建 Docker Compose 文件

创建 /opt/dujiao-next/docker-compose.postgres.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
cd /opt/dujiao-next

cat > docker-compose.postgres.yml <<'EOF'
services:
redis:
image: redis:7-alpine
container_name: dujiaonext-redis
restart: unless-stopped
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD}
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"]
volumes:
- ./data/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 3s
retries: 10
networks:
- dujiao-net

postgres:
image: postgres:16-alpine
container_name: dujiaonext-postgres
restart: unless-stopped
environment:
TZ: ${TZ}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
networks:
- dujiao-net

api:
image: dujiaonext/api:${TAG}
container_name: dujiaonext-api
restart: unless-stopped
environment:
TZ: ${TZ}
DJ_DEFAULT_ADMIN_USERNAME: ${DJ_DEFAULT_ADMIN_USERNAME}
DJ_DEFAULT_ADMIN_PASSWORD: ${DJ_DEFAULT_ADMIN_PASSWORD}
ports:
- "127.0.0.1:${API_PORT}:8080"
volumes:
- ./config/config.yml:/app/config.yml:ro
- ./data/uploads:/app/uploads
- ./data/logs:/app/logs
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"]
interval: 10s
timeout: 3s
retries: 10
networks:
- dujiao-net

user:
image: dujiaonext/user:${TAG}
container_name: dujiaonext-user
restart: unless-stopped
environment:
TZ: ${TZ}
ports:
- "127.0.0.1:${USER_PORT}:80"
depends_on:
api:
condition: service_healthy
networks:
- dujiao-net

admin:
image: dujiaonext/admin:${TAG}
container_name: dujiaonext-admin
restart: unless-stopped
environment:
TZ: ${TZ}
ports:
- "127.0.0.1:${ADMIN_PORT}:80"
depends_on:
api:
condition: service_healthy
networks:
- dujiao-net

networks:
dujiao-net:
driver: bridge
EOF

这里有几个安全点:

1
2
3
4
5
6
Redis 没有暴露 6379 到公网
PostgreSQL 没有暴露 5432 到公网
API 只绑定 127.0.0.1:8080
前台只绑定 127.0.0.1:8081
后台只绑定 127.0.0.1:8082
公网访问统一走 Nginx

十、启动 Dujiao-Next

1
2
3
4
5
cd /opt/dujiao-next

docker compose --env-file .env -f docker-compose.postgres.yml pull
docker compose --env-file .env -f docker-compose.postgres.yml up -d
docker compose --env-file .env -f docker-compose.postgres.yml ps

正常应该看到 5 个容器:

1
2
3
4
5
dujiaonext-redis
dujiaonext-postgres
dujiaonext-api
dujiaonext-user
dujiaonext-admin

并且 Redis、PostgreSQL、API 应该是 healthy。

测试本机服务:

1
2
3
curl -fsS http://127.0.0.1:8080/health
curl -I http://127.0.0.1:8081
curl -I http://127.0.0.1:8082

正常结果大致是:

1
2
3
{"status":"ok"}
HTTP/1.1 200 OK
HTTP/1.1 200 OK

踩坑:只 pull 了,没有真正 up -d

如果你执行完后访问:

1
curl -fsS http://127.0.0.1:8080/health

出现:

1
curl: (7) Failed to connect to 127.0.0.1 port 8080

先检查容器是否真的启动:

1
docker compose --env-file .env -f docker-compose.postgres.yml ps

如果没有容器,就重新执行:

1
docker compose --env-file .env -f docker-compose.postgres.yml up -d

十一、安装并配置 Nginx 反向代理

先确保 Nginx 已安装:

1
apt install -y nginx

创建配置目录:

1
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled

写入 Nginx 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
cat > /etc/nginx/sites-available/dujiao-next.conf <<'EOF'
server {
listen 80;
server_name shop.example.com;

location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location = /sitemap.xml {
proxy_pass http://127.0.0.1:8080/sitemap.xml;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location = /robots.txt {
proxy_pass http://127.0.0.1:8080/robots.txt;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /uploads/ {
proxy_pass http://127.0.0.1:8080/uploads/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

server {
listen 80;
server_name admin.example.com;

location / {
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /uploads/ {
proxy_pass http://127.0.0.1:8080/uploads/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF

启用配置:

1
2
3
4
5
6
ln -sf /etc/nginx/sites-available/dujiao-next.conf /etc/nginx/sites-enabled/dujiao-next.conf
rm -f /etc/nginx/sites-enabled/default

nginx -t
systemctl enable --now nginx
systemctl reload nginx

测试 HTTP:

1
2
curl -I http://shop.example.com
curl -I http://admin.example.com

正常应该看到:

1
HTTP/1.1 200 OK

十二、Nginx 端口占用问题处理

如果启动 Nginx 时出现:

1
bind() to 0.0.0.0:80 failed (98: Address already in use)

说明 80 端口已经被占用。

先查看端口:

1
ss -ltnp | grep -E ':80|:443'

如果发现已有 Nginx 进程占用,但 systemd 认为 Nginx 没启动,可以清理旧进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
nginx -s quit || true
sleep 2

pkill -TERM nginx || true
sleep 2

pkill -9 nginx || true
rm -f /run/nginx.pid /var/run/nginx.pid

systemctl reset-failed nginx
nginx -t
systemctl start nginx
systemctl status nginx --no-pager -l

看到下面状态就正常了:

1
Active: active (running)

十三、申请 HTTPS 证书

确认 Certbot 安装:

1
certbot --version

如果没有安装:

1
2
apt update
apt install -y certbot python3-certbot-nginx

申请证书:

1
2
3
4
5
6
7
certbot --nginx \
-d shop.example.com \
-d admin.example.com \
--email [email protected] \
--agree-tos \
--no-eff-email \
--redirect

成功后会看到类似:

1
2
Successfully received certificate.
Congratulations! You have successfully enabled HTTPS on https://shop.example.com and https://admin.example.com

测试 HTTPS:

1
2
curl -I https://shop.example.com
curl -I https://admin.example.com

检查自动续期:

1
certbot renew --dry-run

十四、访问后台

最终访问地址:

1
2
前台:https://shop.example.com
后台:https://admin.example.com

查看后台初始账号和密码:

1
2
cd /opt/dujiao-next
grep DJ_DEFAULT_ADMIN .env

示例:

1
2
DJ_DEFAULT_ADMIN_USERNAME=admin
DJ_DEFAULT_ADMIN_PASSWORD=Adminxxxxxxxxxxxxxxxxxxxx

登录后台后,第一件事就是修改后台密码。


十五、常用维护命令

进入部署目录:

1
cd /opt/dujiao-next

查看容器状态:

1
docker compose --env-file .env -f docker-compose.postgres.yml ps

查看 API 日志:

1
docker compose --env-file .env -f docker-compose.postgres.yml logs -f api

查看最近 200 行 API 日志:

1
docker compose --env-file .env -f docker-compose.postgres.yml logs --tail=200 api

查看 PostgreSQL 日志:

1
docker compose --env-file .env -f docker-compose.postgres.yml logs --tail=100 postgres

查看 Redis 日志:

1
docker compose --env-file .env -f docker-compose.postgres.yml logs --tail=100 redis

重启服务:

1
docker compose --env-file .env -f docker-compose.postgres.yml restart

停止服务:

1
docker compose --env-file .env -f docker-compose.postgres.yml down

启动服务:

1
docker compose --env-file .env -f docker-compose.postgres.yml up -d

更新镜像:

1
2
3
4
5
cd /opt/dujiao-next

docker compose --env-file .env -f docker-compose.postgres.yml pull
docker compose --env-file .env -f docker-compose.postgres.yml up -d
docker compose --env-file .env -f docker-compose.postgres.yml ps

十六、备份建议

建议定期备份以下目录和文件:

1
2
3
4
5
6
/opt/dujiao-next/.env
/opt/dujiao-next/config/config.yml
/opt/dujiao-next/docker-compose.postgres.yml
/opt/dujiao-next/data/postgres
/opt/dujiao-next/data/uploads
/opt/dujiao-next/data/redis

可以打包备份:

1
2
3
cd /opt

tar -czvf dujiao-next-backup-$(date +%F).tar.gz dujiao-next

如果数据较大,建议只备份关键目录:

1
2
3
4
5
6
7
cd /opt/dujiao-next

tar -czvf /root/dujiao-next-config-$(date +%F).tar.gz \
.env \
config/config.yml \
docker-compose.postgres.yml \
data/uploads

PostgreSQL 更稳妥的备份方式是使用 pg_dump

1
2
3
4
5
6
cd /opt/dujiao-next

docker exec dujiaonext-postgres pg_dump \
-U dujiao \
-d dujiao_next \
> /root/dujiao-next-db-$(date +%F).sql

恢复时可以使用:

1
cat /root/dujiao-next-db-YYYY-MM-DD.sql | docker exec -i dujiaonext-postgres psql -U dujiao -d dujiao_next

十七、最终检查清单

部署完成后,建议按下面顺序检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cd /opt/dujiao-next

docker compose --env-file .env -f docker-compose.postgres.yml ps
curl -fsS http://127.0.0.1:8080/health
curl -I http://127.0.0.1:8081
curl -I http://127.0.0.1:8082

nginx -t
systemctl status nginx --no-pager -l

curl -I http://shop.example.com
curl -I http://admin.example.com

curl -I https://shop.example.com
curl -I https://admin.example.com

certbot renew --dry-run

正常情况下:

1
2
3
4
5
6
7
API health 返回 {"status":"ok"}
前台本地 8081 返回 200
后台本地 8082 返回 200
Nginx active running
HTTP 自动跳转 HTTPS
HTTPS 返回 200
Certbot dry-run 成功

十八、这次部署遇到的几个坑

1. sudo: unable to resolve host

原因是主机名没有写入 /etc/hosts

修复:

1
echo "127.0.1.1 $(hostname)" >> /etc/hosts

2. ModuleNotFoundError: No module named 'yaml'

原因是缺少 Python YAML 模块。

修复:

1
apt install -y python3-yaml

3. 容器没启动,访问 8080/8081/8082 失败

不要只执行 pull,还要执行:

1
docker compose --env-file .env -f docker-compose.postgres.yml up -d

4. Nginx 配置目录不存在

有些环境没有默认创建这两个目录。

修复:

1
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled

5. Nginx 80/443 端口被旧进程占用

检查:

1
ss -ltnp | grep -E ':80|:443'

清理旧 Nginx 进程:

1
2
3
4
5
pkill -TERM nginx || true
pkill -9 nginx || true
rm -f /run/nginx.pid /var/run/nginx.pid
systemctl reset-failed nginx
systemctl start nginx

6. certbot: command not found

原因是 Certbot 没装。

修复:

1
apt install -y certbot python3-certbot-nginx

总结

这次 Dujiao-Next 部署最终使用的是:

1
2
3
4
5
Docker Compose 管理服务
PostgreSQL 存储数据
Redis 做缓存和队列
Nginx 反向代理前台和后台
Certbot 自动申请 HTTPS

最关键的安全点是:

1
2
3
4
5
6
Redis 不暴露公网
PostgreSQL 不暴露公网
API/User/Admin 只监听 127.0.0.1
公网只开放 80/443
后台域名不要使用太明显的 admin 也可以
.env、config.yml、数据库和 uploads 目录一定要备份

部署完成后,就可以通过:

1
2
https://shop.example.com
https://admin.example.com

访问前台和后台。