Files
lzwlab_cn/setup.md
T

528 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 服务器部署记录:nginx 欢迎页(lzwlab.cn
> 说明:服务器凭据见 `Authentication.md`,本文档中密码一律以 `$PASS` 代替。
> 执行方式:本地通过 Paramiko 以 SSH 密码方式登录远程执行(远程用户 ubuntu,sudo 组免密)。
## 0. 环境信息
| 项目 | 值 |
| --- | --- |
| 服务器 IP | 1.15.225.173 |
| 登录用户 | ubuntusudo 组,`sudo -n` 可免密) |
| 系统 | Ubuntu 24.04.4 LTSkernel 6.8.0-124-generic,腾讯云 VM |
| 域名 | lzwlab.cn / www.lzwlab.cnDNS 已解析到 1.15.225.173 |
| 防火墙 | ufw inactive(无需额外放行) |
| 磁盘 | 50G,已用 5.3G |
## 1. 连接与初始状态检查
```bash
# 本地连接(密码 $PASS,取自 Authentication.md
ssh ubuntu@1.15.225.173
# 登录后检查系统
whoami && id && hostname && uname -a
cat /etc/os-release | head -3
# 检查 sudo 免密
sudo -n true && echo sudo-ok
# 检查 nginx(结果:未安装)
command -v nginx && nginx -v
# 检查 80/443 端口占用(结果:空闲)
ss -tlnp | grep -E ":(80|443)" || echo no-80-443
# 检查防火墙(结果:inactive)
sudo -n ufw status
# 检查域名解析(结果:均解析到 1.15.225.173
getent hosts lzwlab.cn www.lzwlab.cn
```
本机验证 DNS(结果:Address: 1.15.225.173):
```bash
nslookup lzwlab.cn
nslookup www.lzwlab.cn
```
## 2. 安装 nginx
```bash
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
```
结果:安装 nginx 1.24.0-2ubuntu7.17nginx + nginx-common)。
安装完成后 systemd 已自动启动 nginx 并注册开机自启(默认站点为 /var/www/html 的欢迎页)。
## 3. 创建站点内容
本地创建 `index.html`(欢迎页,中文标题“欢迎访问 lzwlab.cn”,1219 字节),
通过 SFTP 上传到服务器 /tmp,再用 root 安装到站点目录:
```bash
# 本地:上传文件
# Paramiko SFTP: index.html -> /tmp/lzwlab-index.html
# 远程:
sudo mkdir -p /var/www/lzwlab.cn
sudo install -m 644 -o root -g root /tmp/lzwlab-index.html /var/www/lzwlab.cn/index.html
```
页面内容要点(详见仓库内 `index.html`):
```html
<h1>🎉 欢迎访问 lzwlab.cn</h1>
<span class="badge">Powered by Nginx</span>
```
> 后续变更:2026-08-26 按需求移除了页面中的 `<p>网站搭建成功!这是部署在个人云服务器上的第一个页面。</p>` 提示信息(详见 CHANGELOG)。
## 4. 配置 nginx 站点
创建站点配置 `/etc/nginx/sites-available/lzwlab.cn`(内容同仓库内 `lzwlab.nginx.conf`):
```nginx
server {
listen 80;
listen [::]:80;
server_name lzwlab.cn www.lzwlab.cn;
root /var/www/lzwlab.cn;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
启用站点并移除默认站点:
```bash
# 上传配置
# Paramiko SFTP: lzwlab.nginx.conf -> /tmp/lzwlab.conf
# 远程:
sudo install -m 644 -o root -g root /tmp/lzwlab.conf /etc/nginx/sites-available/lzwlab.cn
sudo ln -sf /etc/nginx/sites-available/lzwlab.cn /etc/nginx/sites-enabled/lzwlab.cn
sudo rm -f /etc/nginx/sites-enabled/default
sudo rm -f /tmp/lzwlab-index.html /tmp/lzwlab.conf
# 校验配置语法
sudo nginx -t
# 结果:syntax is ok / test is successful
```
## 5. 启动 / 加载配置
```bash
sudo systemctl enable nginx
sudo systemctl start nginx
# 注意:apt 安装时 nginx 已启动并加载了默认配置,
# 所以修改配置后必须 reload 才会生效:
sudo nginx -t
sudo systemctl reload nginx
```
检查监听状态(结果:0.0.0.0:80 与 [::]:80 均已监听):
```bash
sudo systemctl is-active nginx # active
ss -tlnp | grep :80
```
## 6. 验证
服务器本机验证:
```bash
curl -s -o /dev/null -w "status=%{http_code} size=%{size_download}\n" http://127.0.0.1/ -H "Host: lzwlab.cn"
# status=200 size=1219
curl -s http://127.0.0.1/ -H "Host: lzwlab.cn" | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
```
本地(公网域名)验证:
```bash
curl -s -o /dev/null -w "status=%{http_code} size=%{size_download}\n" http://lzwlab.cn/
# status=200 size=1219
curl -s http://lzwlab.cn/ | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
curl -s -o /dev/null -w "status=%{http_code}\n" http://www.lzwlab.cn/
# status=200
```
✅ 部署完成:http://lzwlab.cn 与 http://www.lzwlab.cn 均已正常展示欢迎页。
## 7. 部署 HTTPS 证书并配置 443 自动跳转
### 7.1 证书信息
本地证书目录 `lzwlab.cn_nginx/`
| 文件 | 用途 |
| --- | --- |
| `lzwlab.cn.key` | RSA 私钥(1700 字节) |
| `lzwlab.cn_bundle.crt` / `.pem` | 证书 + 证书链(两者内容相同) |
| `lzwlab.cn.csr` | 证书签名请求(部署用不到) |
证书校验(本机 openssl):
```bash
openssl x509 -in lzwlab.cn_nginx/lzwlab.cn_bundle.crt -noout -subject -issuer -dates
# subject=CN=lzwlab.cn
# issuer=TrustAsia DV TLS RSA CA 2024
# 有效期:2026-08-24 ~ 2026-11-22
openssl rsa -in lzwlab.cn_nginx/lzwlab.cn.key -check -noout # RSA key ok
```
SAN 包含:`DNS:lzwlab.cn``DNS:www.lzwlab.cn`
### 7.2 上传证书与配置到服务器
```bash
# 本地:SFTP 上传到 /tmp
# lzwlab.cn_nginx/lzwlab.cn.key -> /tmp/lzwlab.cn.key
# lzwlab.cn_nginx/lzwlab.cn_bundle.crt -> /tmp/lzwlab.cn_bundle.crt
# lzwlab.nginx.conf -> /tmp/lzwlab.conf
# 远程:安装证书/私钥并更新站点配置
sudo mkdir -p /etc/nginx/ssl
sudo install -m 600 -o root -g root /tmp/lzwlab.cn.key /etc/nginx/ssl/lzwlab.cn.key
sudo install -m 644 -o root -g root /tmp/lzwlab.cn_bundle.crt /etc/nginx/ssl/lzwlab.cn_bundle.crt
sudo cp /etc/nginx/sites-available/lzwlab.cn /etc/nginx/sites-available/lzwlab.cn.bak
sudo install -m 644 -o root -g root /tmp/lzwlab.conf /etc/nginx/sites-available/lzwlab.cn
sudo rm -f /tmp/lzwlab.cn.key /tmp/lzwlab.cn_bundle.crt /tmp/lzwlab.conf
```
私钥权限 600、root:root;证书 644。旧配置备份为 `lzwlab.cn.bak`
### 7.3 更新后的站点配置(`/etc/nginx/sites-available/lzwlab.cn`
```nginx
server {
listen 80;
listen [::]:80;
server_name lzwlab.cn www.lzwlab.cn;
return 301 https://$host$request_uri; # HTTP 全部 301 跳转到 HTTPS
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name lzwlab.cn www.lzwlab.cn;
ssl_certificate /etc/nginx/ssl/lzwlab.cn_bundle.crt;
ssl_certificate_key /etc/nginx/ssl/lzwlab.cn.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/lzwlab.cn;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
### 7.4 校验并加载
```bash
sudo nginx -t # syntax is ok / test is successful
sudo systemctl reload nginx
```
### 7.5 验证结果
服务器本机:
```bash
curl -s -o /dev/null -w "http status=%{http_code} redirect=%{redirect_url}\n" http://127.0.0.1/ -H "Host: lzwlab.cn"
# status=301 redirect=https://lzwlab.cn/
curl -s https://127.0.0.1/ -k -H "Host: lzwlab.cn" | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
```
本地公网:
```bash
curl -s -o /dev/null -w "status=%{http_code} -> %{redirect_url}\n" http://lzwlab.cn/
# status=301 -> https://lzwlab.cn/
curl -s -o /dev/null -w "status=%{http_code} ssl_verify=%{ssl_verify_result}\n" https://lzwlab.cn/
# status=200 ssl_verify=0(证书链受信任)
curl -s -o /dev/null -w "status=%{http_code}\n" https://www.lzwlab.cn/
# status=200
echo | openssl s_client -connect lzwlab.cn:443 -servername lzwlab.cn -alpn h2,http/1.1 2>/dev/null | grep "ALPN"
# ALPN protocol: h2HTTP/2 已启用)
```
✅ https://lzwlab.cn 与 https://www.lzwlab.cn 已启用 HTTPShttp 访问会自动 301 跳转到 https。
## 8. 部署“文件快传”工具(/transfer/)
### 8.1 方案
- 后端:Flask 3.0.2 + gunicorn 20.1apt 安装),监听 `127.0.0.1:8090`systemd 服务 `lzwlab-transfer.service`
- 前端:单页 HTML(内嵌 CSS/JS),PC 与手机自适应,登录后可用。
- 存储:上传文件保存在 `/var/lib/lzwlab-transfer/files/`,元数据在 SQLite `/var/lib/lzwlab-transfer/files.db`
- 配置:`/etc/lzwlab-transfer/config.json`root:lzwlab 640),登录密码只保存 scrypt 哈希,明文经部署环境变量传入。
- 清理:`lzwlab-transfer-cleanup.timer` 每 10 分钟执行 `cleanup.py` 删除过期文件;访问列表/手动清理时也会触发检查。
- nginx`/transfer/` 反向代理到 8090`proxy_request_buffering off` + `proxy_buffering off` 保证大文件上传下载流式传输,`client_max_body_size 5120m`
### 8.2 部署命令(本地运维机)
```bash
# 依赖脚本见仓库 deploy_transfer.py;密码经环境变量传入,不会写入仓库
LZWLAB_APP_PASSWORD='$PASS' python deploy_transfer.py --username admin
```
脚本会依次完成:apt 安装依赖 → 创建系统用户 `lzwlab` 与目录 → 上传/安装应用、配置、systemd 单元 → 启动服务与清理定时器 → 校验并 reload nginx。重复执行幂等。
### 8.3 验证结果
```bash
systemctl is-active lzwlab-transfer.service lzwlab-transfer-cleanup.timer # active active
curl -s -o /dev/null -w "%{http_code}\n" https://lzwlab.cn/transfer/ # 200(登录页)
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://lzwlab.cn/transfer/ # 301 -> https
```
公网 API 实测:登录成功返回会话 Cookie;未登录访问 `/api/files` 返回 401;上传 25 字节测试文件后列表可查,下载返回 200 且支持 `Range`(206);删除后列表为空;将测试文件过期时间强制置为过去并启动清理服务后,日志输出“清理完成:过期文件 1 个”,列表随即清空。
### 8.4 踩坑
- `deploy_transfer.py` 在 Windows 本地生成远端路径时不能使用 `os.path.join`(会把 `/` 变成 `\`),SQLite 路径必须手工拼接 Linux 路径,否则服务报 `unable to open database file`
- gunicorn 手动排障时不要在 SSH 会话里前台运行(会话断开会留下占端口的孤儿进程),应直接通过 `systemctl` 管理;排障后可 `sudo pkill -u lzwlab -f gunicorn && sudo systemctl restart lzwlab-transfer.service`
- Windows 下 curl 的 `-F 'file=@...;filename=中文名'` 对非 ASCII 文件名支持不佳,测试时用 ASCII 文件名,浏览器上传不受影响。
## 9. 部署 RustDesk 自建服务器(Docker
### 9.1 方案
- 组件:RustDesk Server OSS 版两个容器——hbbs(ID/注册服务器)与 hbbr(中继服务器),镜像 `rustdesk/rustdesk-server:latest`
- 网络:`network_mode: host`(官方推荐,无需 `-p` 映射),数据卷 `./data` 挂载到容器 `/root`(密钥、SQLite 库)。
- 端口(防火墙需放行):
- hbbs21115/TCPNAT 类型测试)、21116/TCP+UDPUDP 用于 ID 注册与心跳,TCP 用于打洞与连接服务)、21118/TCP(网页客户端);
- hbbr21117/TCP(中继服务)、21119/TCP(网页客户端);
- 21114/TCP 网页控制台仅 Pro 版需要,OSS 版不监听。
- 参考文档:https://rustdesk.com/docs/zh-cn/self-host/rustdesk-server-oss/docker/ ;客户端配置:https://rustdesk.com/docs/zh-cn/self-host/client-configuration/
### 9.2 安装 Docker 并配置镜像加速
```bash
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
docker --version # Docker version 29.1.3apt docker.io 自带守护进程服务)
```
踩坑:腾讯云访问 Docker Hub 被拒(`registry-1.docker.io` connection refused),必须配置镜像加速:
```bash
echo '{"registry-mirrors":["https://docker.m.daocloud.io"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
```
### 9.3 部署 hbbs/hbbr
Compose 文件为仓库内 `rustdesk/docker-compose.yml`
```yaml
services:
hbbs:
container_name: hbbs
image: rustdesk/rustdesk-server:latest
command: hbbs
volumes:
- ./data:/root
network_mode: "host"
depends_on:
- hbbr
restart: unless-stopped
hbbr:
container_name: hbbr
image: rustdesk/rustdesk-server:latest
command: hbbr
volumes:
- ./data:/root
network_mode: "host"
restart: unless-stopped
```
部署命令:
```bash
# 本地:SFTP 上传 rustdesk/docker-compose.yml 到 /tmp
sudo mkdir -p /opt/rustdesk
sudo install -m 644 -o root -g root /tmp/rustdesk-compose.yml /opt/rustdesk/compose.yml
cd /opt/rustdesk && sudo docker compose up -d
```
验证结果:
```bash
sudo docker ps # hbbs、hbbr 均 Up
sudo ss -tlnup | grep -E '2111[4-9]'
# tcp 21115/21116、udp 21116、tcp 21118 归 hbbstcp 21117/21119 归 hbbr
sudo docker logs --tail 5 hbbs
# Key: UB1fNGcVYA9NTIEA7ra3Nm0R6o2m64lNQE24L7CIAkY=
# Listening on tcp/udp :21116 / Listening on tcp :21115 / Listening on websocket :21118
```
### 9.4 客户端配置
RustDesk 客户端(设置 → 网络,点击“解锁”后可编辑)填写:
| 项目 | 值 |
| --- | --- |
| ID 服务器 | `lzwlab.cn`hbbs,端口 21116 |
| 中继服务器 | 留空自动推导;或填 `lzwlab.cn`hbbr,端口 21117 |
| Key | `UB1fNGcVYA9NTIEA7ra3Nm0R6o2m64lNQE24L7CIAkY=` |
| API 服务器 | 留空(仅 Pro 版需要) |
- Key 由 hbbs 首次启动自动生成,保存在服务器 `/opt/rustdesk/data/id_ed25519.pub`(容器内 `/root/id_ed25519.pub`),重建容器会复用;Key 需分发给每个客户端。
- 连通性:本地已验证 21115-21119 TCP 公网可达;hbbs 日志可见外部客户端注册记录。UDP 21116 用于 ID 注册与心跳,若腾讯云控制台防火墙未放行 UDP 21116,客户端将无法注册。
### 9.5 运维
```bash
# 更新镜像并重建
cd /opt/rustdesk && sudo docker compose pull && sudo docker compose up -d
# 查看日志
sudo docker logs -f hbbs
sudo docker logs -f hbbr
# 查看密钥
sudo cat /opt/rustdesk/data/id_ed25519.pub
```
## 10. 部署 Gitea Git 服务(/git/
### 10.1 方案
- 组件:Gitea 1.27.2 官方镜像 `docker.gitea.com/gitea:1.27.2`Docker Compose,容器名 `gitea`),数据库 SQLite,数据卷 `./gitea:/data`(持久化在服务器 `/opt/gitea/gitea/`)。
- 入口:服务器没有 `git.lzwlab.cn` 子域名解析,按 Gitea 官方反向代理文档的 sub-path 方案部署在 `https://lzwlab.cn/git/``ROOT_URL=https://lzwlab.cn/git/`)。
- 端口:Web 仅监听 `127.0.0.1:3000`(不对公网直接暴露),由 nginx 将 `/git/` 反代到 3000;SSH 端口与主机 sshd 冲突,容器内 22 映射到主机 2222。
- 账号:管理员私有单实例——关闭开放注册(`DISABLE_REGISTRATION=true`)、锁定安装器(`INSTALL_LOCK=true`),管理员 `admin`(密码见 Authentication.md)。
- 参考文档:https://docs.gitea.com/installation/install-with-docker/ 与 https://docs.gitea.com/administration/reverse-proxies/ (“Nginx with a sub-path”)。
### 10.2 部署命令
Compose 文件为仓库内 `gitea/docker-compose.yml`
```bash
# 本地:SFTP 上传 gitea/docker-compose.yml 到 /tmp
sudo mkdir -p /opt/gitea
sudo install -m 644 -o root -g root /tmp/gitea-compose.yml /opt/gitea/compose.yml
sudo rm -f /tmp/gitea-compose.yml
cd /opt/gitea && sudo docker compose up -d
```
创建管理员并设置站点名:
```bash
# 管理员(密码经变量传入,不写入仓库;首次启动完成后执行)
sudo docker exec -u git gitea gitea admin user create \
--username admin --password '$PASS' --email admin@lzwlab.cn \
--admin --must-change-password=false
# 站点名写入 app.ini 的 [DEFAULT] 段后重建容器
sudo sed -i '1s/.*/APP_NAME = lzwlab Git 服务/' /opt/gitea/gitea/gitea/conf/app.ini
cd /opt/gitea && sudo docker compose up -d
```
### 10.3 nginx 反代(/git/
按官方 “Nginx with a sub-path” 方案(保持 `%2F` 原样并剥离 `/git` 前缀),在 443 server 块新增:
```nginx
location ~ ^/(git)($|/) {
client_max_body_size 512M;
rewrite ^ $request_uri;
rewrite ^/(git($|/))?(.*) /$3 break;
proxy_pass http://127.0.0.1:3000$uri;
proxy_http_version 1.1;
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
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;
proxy_read_timeout 300s;
}
```
该配置已并入仓库内 `lzwlab.nginx.conf`,服务器 `/etc/nginx/sites-available/lzwlab.cn` 同步更新(改前自动备份 `lzwlab.cn.bak-YYYYMMDD-HHMMSS`),`sudo nginx -t` 通过后 reload。
### 10.4 验证结果
```bash
curl -s -o /dev/null -w "%{http_code}\n" https://lzwlab.cn/git/ # 200
curl -s https://lzwlab.cn/git/api/v1/version # {"version":"1.27.2"}
curl -s https://lzwlab.cn/git/ | grep -o '<title>.*</title>' # <title>lzwlab Git 服务</title>
curl -s https://lzwlab.cn/ | grep -o '进入 Git 服务' # 进入 Git 服务(首页按钮已生效)
# git 智能 HTTP 协议(创建测试仓库 hello 后):
curl -s 'https://lzwlab.cn/git/admin/hello.git/info/refs?service=git-upload-pack'
# 返回 service=git-upload-pack 与 HEAD 引用;测试仓库与临时令牌验证后已删除
```
关键配置项(`/opt/gitea/gitea/gitea/conf/app.ini`):`ROOT_URL=https://lzwlab.cn/git/``DOMAIN/SSH_DOMAIN=lzwlab.cn``SSH_PORT=2222``SSH_LISTEN_PORT=22`(容器内)、`INSTALL_LOCK=true``DISABLE_REGISTRATION=true``APP_NAME=lzwlab Git 服务`
### 10.5 使用与运维
- 克隆地址:
- HTTPS(公网可用):`https://lzwlab.cn/git/<用户名>/<仓库>.git`
- SSH(需先放行安全组,见 10.6):`ssh://git@lzwlab.cn:2222/<用户名>/<仓库>.git`
- 运维命令:
```bash
sudo docker compose -f /opt/gitea/compose.yml ps # 容器状态
sudo docker logs -f gitea # 日志
cd /opt/gitea && sudo docker compose pull && sudo docker compose up -d # 升级镜像
sudo docker exec -u git gitea gitea admin user list # 用户列表
```
- 备份:SQLite 库、仓库与配置全部在数据卷内,直接备份 `/opt/gitea/gitea/` 即可。
### 10.6 踩坑
- `GITEA__APP_NAME` 环境变量传入容器后未生效(站点名仍显示默认值),站点名需直接写入 `app.ini``[DEFAULT]` 段(`APP_NAME = ...`)并重建容器。
- 腾讯云安全组只放行 22/443/21115-21119,主机映射的 2222 端口公网不可达(本机探测 TIMEOUT,服务器本地可达):SSH 克隆需在腾讯云控制台为 TCP 2222 添加入站规则;HTTPS 克隆与网页访问不受影响。
- Gitea 子路径部署必须同时满足两点:`ROOT_URL``/git/` 后缀,且 nginx 使用官方 rewrite 方案剥离前缀(直接 `proxy_pass http://127.0.0.1:3000/;` 会破坏前缀与 `%2F` 转义)。
- 镜像使用官方新仓库 `docker.gitea.com/gitea:1.27.2`(腾讯云可直连,无需镜像加速);若改用 Docker Hub 的 `gitea/gitea` 则会走 daocloud 加速。
## 11. Komari Lite 服务器监控(已卸载)
> 该服务曾部署到 https://status.lzwlab.cn/,后按需求从服务器卸载,首页入口已删除。此前的部署配置与完整记录见 git 历史(commit bdffcee / 60b9ce1)。nginx server 级 return 的踩坑保留在下方“附:踩坑记录”第 6 条。
卸载命令:
```bash
cd /opt/komari && sudo docker compose down
sudo docker rmi ghcr.io/nuomiiiii/komari:latest ghcr.nju.edu.cn/nuomiiiii/komari:latest
sudo rm -f /etc/nginx/sites-enabled/status.lzwlab.cn /etc/nginx/sites-available/status.lzwlab.cn /etc/nginx/sites-available/status.lzwlab.cn-http /etc/nginx/sites-available/status.lzwlab.cn.bak-*
sudo certbot delete --cert-name status.lzwlab.cn --non-interactive
sudo rm -rf /var/www/certbot /opt/komari
sudo nginx -t && sudo systemctl reload nginx
```
## 附:踩坑记录
1. 本地 Git Bash 调用 Windows Python 时,MSYS 会把以 `/` 开头的命令行参数(如 `/tmp/...`)转换成 Windows 路径,
导致 SFTP 上传远端路径错误(ENOENT)。解决:调用前加 `MSYS_NO_PATHCONV=1`,或把路径写进脚本内。
2. apt 安装 nginx 会自动启动服务并加载默认站点配置;改完站点配置后 `systemctl start` 不会重新加载,
必须 `systemctl reload nginx`(或 `restart`),否则看到的仍是默认欢迎页。
3. 该服务器上的 nginx 1.24(Ubuntu 构建)不识别独立指令 `http2 on;``nginx -t` 报 unknown directive "http2"
启用 HTTP/2 应写成 `listen 443 ssl http2;`
4. 腾讯云服务器访问 Docker Hub 被拒(pull 报 connection refused),通过 `/etc/docker/daemon.json` 配置
`registry-mirrors`docker.m.daocloud.io)后解决。
5. `remote.py``--timeout` 参数必须放在远程命令之前(`python remote.py --timeout 500 "命令"`);
放在命令之后会被 argparse 当作远程命令的一部分传给服务器执行。
6. nginx 中 server 级的 `return` 在 rewrite 阶段(location 匹配之前)执行并直接终结请求:`server { location /.well-known/acme-challenge/ {...} return 301 ...; }` 的挑战路径照样会被 301,导致 certbot 签发失败(LE 跟随 301 到 HTTPS 后得到 404)。必须把重定向写进 `location / { return 301 ...; }` 内,让 location 先参与匹配。