Skip to content

我的VPS服务部署记录

2023-01-25

本文记录了在 CentOS 7 VPS 服务器上部署各种服务的详细步骤,包括服务器初始化、Docker 环境配置、以及多个自托管服务的部署过程。

我的 VPS 使用的是 CentOS 7 服务器,所以以下操作都是基于 CentOS 系统。

服务器设置

备份旧的 yum 源:

mkdir -p /etc/yum.repos.d/bak
mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak/

配置 CentOS 7 源:

curl -o /etc/yum.repos.d/Centos7-aliyun.repo https://mirrors.wlnmp.com/centos/Centos7-aliyun-x86_64.repo

清理缓存并重建缓存:

yum clean all
yum makecache

更新 yum 源:

yum update

安装常用软件:

yum install git vim jq ntp -y

设置时钟同步:

systemctl start ntpd
systemctl enable ntpd
ntpdate pool.ntp.org

设置时区为上海时区:

# 查看当前时区
timedatectl
# 设置时区为上海(Asia/Shanghai)
timedatectl set-timezone Asia/Shanghai
# 验证时区设置
timedatectl
date

[可选] 设置系统 Swap 交换分区

因为 vps 服务器的运行内存很小,所以这里先设置下 Swap

# 1GB RAM with 2GB Swap
sudo fallocate -l 2G /swapfile && \
sudo dd if=/dev/zero of=/swapfile bs=1024 count=2097152 && \
sudo chmod 600 /swapfile && \
sudo mkswap /swapfile && \
sudo swapon /swapfile && \
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstab && \
sudo swapon --show && \
sudo free -h

安装 Nginx

参考 CentOS 7 下 yum 安装和配置 Nginx ,使用 yum 安装:

rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
yum install nginx -y
systemctl enable nginx
systemctl start nginx

打开防火墙端口:

yum install firewalld -y
firewall-cmd --zone=public --permanent --add-service=http
firewall-cmd --zone=public --permanent --add-service=https
firewall-cmd --reload

使用反向代理需要打开 SELinux 网络访问权限:

setsebool -P httpd_can_network_connect on

安装并生成证书

域名托管在 CloudFlare,使用 acme.sh 通过 DNS API 自动申请和续期 SSL 证书。参考 acme.sh DNS API 文档

准备工作

  1. 在 CloudFlare 获取 API Token 或 Global API Key
  2. 创建 SSL 证书存储目录
# 创建 SSL 证书目录
mkdir -p /etc/nginx/ssl
# 安装 acme.sh
curl https://get.acme.sh | sh -s email=ichensoul@gmail.com
# 配置 CloudFlare API 凭证(使用 API Token 或 Global API Key)
export CF_Key=XXXXXXXXXXXXXXXXXX # CloudFlare Global API Key
export CF_Email=ichensoul@gmail.com
# 或者使用 API Token(推荐)
# export CF_Token="your_api_token"
# export CF_Account_ID="your_account_id"
# 申请证书(支持通配符域名)
~/.acme.sh/acme.sh --issue --server letsencrypt --dns dns_cf -d chensoul.cc -d '*.chensoul.cc'
# 复制证书到 Nginx 目录
cp ~/.acme.sh/chensoul.cc_ecc/{chensoul.cc.cer,chensoul.cc.key,fullchain.cer,ca.cer} /etc/nginx/ssl/
# 安装证书并设置自动续期
~/.acme.sh/acme.sh --installcert -d chensoul.cc -d *.chensoul.cc \
--cert-file /etc/nginx/ssl/chensoul.cc.cer \
--key-file /etc/nginx/ssl/chensoul.cc.key \
--fullchain-file /etc/nginx/ssl/fullchain.cer \
--ca-file /etc/nginx/ssl/ca.cer \
--reloadcmd "nginx -s reload"

注意:证书会自动续期,续期命令已添加到 crontab 中。

Docker 安装和配置

一键安装docker和docker compose:

bash <(curl -sSL https://linuxmirrors.cn/docker.sh)

启动 Docker 服务:

systemctl enable docker
systemctl start docker
# 验证安装
docker --version
docker ps

查看docker配置文件,默认会给我们配置刚才选择的镜像加速地址

cat /etc/docker/daemon.json

设置 iptables 允许流量转发:

iptables -P FORWARD ACCEPT

参考 Best Practice: Use a Docker network,创建一个自定义的网络:

docker network create vps

查看 docker 网络:

docker network ls
NETWORK ID NAME DRIVER SCOPE
68f4aeaa57bd bridge bridge local
6a96b9d8617e vps bridge local
4a8679e35f4d host host local
ba21bef23b04 none null local

注意:bridge、host、none 是内部预先创建的网络。

服务部署

Postgres

PostgreSQL 是一个功能强大的开源关系型数据库管理系统,作为其他服务的数据存储后端。

创建 postgres.yaml 文件:

services:
postgres:
image: postgres:18
restart: always
ports:
- "5435:5432"
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: vps@027!
TZ: Asia/Shanghai
healthcheck:
test:
["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
interval: 5s
timeout: 10s
retries: 5
networks:
- vps
networks:
vps:
external: true

启动服务:

docker compose -f postgres.yaml up -d
# 查看日志
docker logs -f postgres
# 验证数据库连接
docker exec -it postgres psql -U postgres -d postgres

Umami

Umami 是一个简单、快速、注重隐私的网站分析工具,可以作为 Google Analytics 的开源替代品。

  1. 在 postgres 容器创建 umami 数据库:
docker exec -it postgres psql -U postgres
CREATE DATABASE umami OWNER postgres;
  1. 通过 docker compose 安装,创建 umami.yaml
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: umami
ports:
- "3003:3000"
environment:
DATABASE_URL: postgresql://postgres:vps@027!@postgres:5432/umami
DATABASE_TYPE: postgresql
HASH_SALT: vps@2025
TRACKER_SCRIPT_NAME: random-string.js
TZ: "Asia/Shanghai"
GENERIC_TIMEZONE: "Asia/Shanghai"
networks:
- vps
restart: always
networks:
vps:
external: true

参考 https://eallion.com/umami/,Umami 的默认跟踪代码是被大多数的广告插件屏蔽的,被屏蔽了你就统计不到访客信息了。如果需要反屏蔽,需要在 docker compose.yml 文件中添加环境变量:TRACKER_SCRIPT_NAME,如:

environment:
TRACKER_SCRIPT_NAME: random-string.js

然后获取到的跟踪代码的 src 会变成:

srcipt.js => random-string.js

启动:

docker compose -f umami.yaml up -d
docker logs -f umami
  1. 设置自定义域名:umami.chensoul.cc

  2. 配置 nginx 配置文件 /etc/nginx/conf.d/umami.conf

server {
listen 80;
listen [::]:80;
server_name umami.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name umami.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:3003;
proxy_set_header Host $http_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;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
expires 12h;
}
}
  1. 重新加载 Nginx 配置并访问服务:
nginx -t
nginx -s reload

访问 https://umami.chensoul.cc/,默认用户名和密码为 admin/umami。登录之后,请立即修改密码,并添加要统计的网站。

Memos

Memos 是一个开源的、自托管的备忘录和知识管理系统,支持 Markdown、标签、搜索等功能。

  1. 在 postgres 容器创建 memos 数据库:
docker exec -it postgres psql -U postgres
CREATE DATABASE memos OWNER postgres;
  1. 通过 docker compose 安装,创建 memos.yaml
services:
memos:
image: neosmemo/memos:0.18.2
container_name: memos
environment:
- MEMOS_DRIVER=postgres
- MEMOS_DSN=postgres://postgres:vps@027!@postgres:5432/memos?sslmode=disable
volumes:
- ~/.memos/:/var/opt/memos
ports:
- '5230:5230'
networks:
- vps
networks:
vps:
external: true

版本说明

  • 0.18.2 版本:功能丰富,包括电报机器人、webhook、S3 存储和自定义 CDN 域名等。支持多数据库(PostgreSQL、MySQL、SQLite)。
  • 0.14.4 版本:轻量级版本,无评论功能,仅支持 SQLite3。
  1. 启动服务:
docker compose -f memos.yaml up -d
docker logs -f memos
  1. 配置 nginx 配置文件 /etc/nginx/conf.d/memos.conf
server {
listen 80;
listen [::]:80;
server_name memos.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name memos.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:5230;
proxy_set_header Host $http_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;
}
}
  1. 自定义样式(可选):

美化标签样式,在 Memos 设置中添加自定义 CSS:

.status-text{font-size:10px !important;border:none;color:rgb(156,163,175) !important;}
.tag-span,.dark .tag-span{border: 1px solid;border-radius:6px;padding:0px 6px;color:rgb(22,163,74) !important;font-size:12px !important;-webkit-transform: scale(calc(10 / 12));transform-origin: left center;}
.memo-content-text .link{color:rgb(22,163,74) !important;margin-right:-6px;}
header .bg-blue-600{display:none !important;}
.text-lg {font-size: 1rem !important;}
.header-wrapper,.sidebar-wrapper{width: 11rem !important;}
.filter-query-container{padding-bottom:0.5rem;}
  1. 重新加载 Nginx 配置并访问服务:
nginx -s reload

访问 https://memos.chensoul.cc/ ,默认用户名和密码为 admin/memos。登录之后,请立即修改密码。

N8n

N8n 是一个开源的工作流自动化工具,可以通过可视化界面创建自动化工作流,连接各种服务和 API。

  1. 在 postgres 容器创建 n8n 数据库:
docker exec -it postgres psql -U postgres
CREATE DATABASE n8n OWNER postgres;
  1. 通过 docker compose 安装,创建 n8n.yaml
services:
n8n:
image: n8nio/n8n
container_name: n8n
restart: always
environment:
- NODE_ENV=production
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=postgres
- DB_POSTGRESDB_PASSWORD=vps@027!
- TZ="Asia/Shanghai"
- GENERIC_TIMEZONE="Asia/Shanghai"
- N8N_DEFAULT_LOCALE=zh-CN
# 是否启用诊断和个性化推荐
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_PERSONALIZATION_ENABLED=false
# 是否在启动时重新安装缺失的包
- N8N_REINSTALL_MISSING_PACKAGES=true
- WEBHOOK_URL=https://n8n.chensoul.cc/
- N8N_HOST=n8n.chensoul.cc
- N8N_ENCRYPTION_KEY=2cc5b5f9e31b43ff3817c1d04e5fa735
ports:
- '5678:5678'
networks:
- vps
networks:
vps:
external: true
  1. 启动服务:
docker compose -f n8n.yaml up -d
docker logs -f n8n
  1. 配置 nginx 配置文件 /etc/nginx/conf.d/n8n.conf
server {
listen 80;
listen [::]:80;
server_name n8n.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name n8n.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_set_header Host $http_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_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Origin 'https://n8n.chensoul.cc';
access_log /var/log/nginx/n8n.log combined buffer=128k flush=5s;
}
}

故障排查:如果出现 Origin header does NOT match the expected origin. (Origin: "n8n.chensoul.cc", Expected: "127.0.0.1:5678") 错误,需要在 nginx 配置里添加 proxy_set_header Origin 'https://n8n.chensoul.cc';

  1. 重新加载 Nginx 配置并访问服务:
nginx -t
nginx -s reload
  1. 创建工作流(Workflows):

参考这篇文章 http://stiles.cc/archives/237/ ,目前我配置了以下 workflows,实现了 github、douban、rss、memos 同步到 Telegram。

workflows 参考:

Linkding

一款自托管的书签管理器,设计简洁、快速且易于设置。

创建数据库

docker exec -it postgres psql -U postgres
CREATE DATABASE linkding OWNER postgres;

创建 docker-compose 文件 linkding.yaml

services:
linkding:
#image: woohoodai/linkding-cn:latest
image: sissbruecker/linkding
container_name: linkding
restart: unless-stopped
environment:
- LD_SUPERUSER_NAME=admin
- LD_SUPERUSER_PASSWORD=E2KWdEGCEF7Bihx!98
- LD_DB_ENGINE=postgres
- LD_DB_DATABASE=linkding
- LD_DB_HOST=postgres
- LD_DB_USER=postgres
- LD_DB_PASSWORD=vps@027!
ports:
- "9090:9090"
networks:
- vps
networks:
vps:
external: true

启动服务:

docker compose -f linkding.yaml up -d
docker logs -f linkding

配置 nginx 配置文件 /etc/nginx/conf.d/linkding.conf

server {
listen 80;
listen [::]:80;
server_name linkding.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name linkding.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:9090;
proxy_set_header Host $http_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_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
access_log /var/log/nginx/linkding.log combined buffer=128k flush=5s;
}
}

重新加载 Nginx 配置并访问服务:

nginx -t
nginx -s reload

Bark

Bark 是一个 iOS 应用,允许你向 iPhone 推送自定义通知。通过自建服务器,可以实现消息推送的完全控制。

项目地址:https://github.com/Finb/Bark

  1. 服务端使用 Docker 安装:
docker run -dt --name bark -p 3020:8080 -v ~/.bark/data://data finab/bark-server
  1. 配置 nginx 配置文件 /etc/nginx/conf.d/bark.conf
server {
listen 80;
listen [::]:80;
server_name bark.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name bark.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:3020;
proxy_set_header Host $http_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;
}
}
  1. 重新加载 Nginx 配置:
nginx -t
nginx -s reload
  1. 在 iOS 上安装 Bark 应用,添加私有服务器:`http://bark.chensoul.cc

  2. 测试推送功能:

# 替换 <device_key> 为你的设备密钥
curl -X "POST" "https://bark.chensoul.cc/<device_key>" \
-H 'Content-Type: application/json; charset=utf-8' \
-d '{
"body": "这是一个测试",
"title": "Test Title",
"badge": 1,
"category": "myNotificationCategory",
"icon": "https://www.usememos.com/logo-rounded.png",
"group": "test"
}'

Uptime Kuma

Uptime Kuma 是一个易于使用的自托管监控工具,可以监控网站和服务的可用性。

项目地址:https://github.com/louislam/uptime-kuma

使用 docker compose 部署,创建 uptime.yaml

services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ~/.uptime-kuma://app/data
ports:
- 3001:3001
restart: always
networks:
- vps
networks:
vps:
external: true

启动服务:

docker compose -f uptime.yaml up -d
docker logs -f uptime-kuma

配置 nginx 配置文件 /etc/nginx/conf.d/uptime.conf

server {
listen 80;
listen [::]:80;
server_name uptime.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name uptime.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $http_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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

重新加载 Nginx 配置并访问服务:

nginx -t
nginx -s reload

访问 https://uptime.chensoul.cc/,首次访问需要设置管理员账号。

升级服务

docker compose -f uptime.yaml down
docker pull louislam/uptime-kuma:1
docker compose -f uptime.yaml up -d

Artalk

在 postgres 容器创建 artalk 数据库:

docker exec -it postgres psql -U postgres
CREATE DATABASE artalk OWNER postgres;

通过 docker compose 安装,创建 artalk.yaml

services:
artalk:
container_name: artalk
image: artalk/artalk-go
restart: unless-stopped
ports:
- 23366:23366
environment:
- TZ=Asia/Shanghai
- ATK_LOCALE=zh-CN
- ATK_SITE_DEFAULT=Chensoul Blog
- ATK_SITE_URL=https://blog.chensoul.cc
- ATK_TRUSTED_DOMAINS=https://blog.chensoul.cc http://localhost:1313
- ATK_DB_TYPE=pgsql
- ATK_DB_HOST=postgres
- ATK_DB_PORT=5432
- ATK_DB_NAME=artalk
- ATK_DB_USER=postgres
- ATK_DB_PASSWORD=vps@027!
- ATK_ADMIN_NOTIFY_BARK_ENABLED=true
- ATK_ADMIN_NOTIFY_BARK_SERVER=https://bark.chensoul.cc/AXWM5ZKnKa9Lp3rijBsVDm
- ATK_ADMIN_NOTIFY_TELEGRAM_ENABLED=true
- ATK_ADMIN_NOTIFY_TELEGRAM_API_TOKEN=8286091453:AAE2pucQXfliz_QGac1zVZEFIrrxVOLe938
- ATK_ADMIN_NOTIFY_TELEGRAM_RECEIVERS=[-1001632154815]
networks:
- vps
networks:
vps:
external: true

启动服务:

docker compose -f artalk.yaml up -d
docker logs -f artalk

配置 nginx 配置文件 /etc/nginx/conf.d/artalk.conf

server {
listen 80;
listen [::]:80;
server_name artalk.chensoul.cc;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name artalk.chensoul.cc;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/chensoul.cc.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:23366;
proxy_set_header Host $http_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;
}
}

重新加载 Nginx 配置:

nginx -t
nginx -s reload

执行命令创建管理员账户:

docker exec -it artalk artalk admin

浏览器输入 http://artalk.chensoul.cc 进入 Artalk 后台登录界面。

服务升级

使用 Watchtower 自动更新 Docker 镜像与容器。Watchtower 会定期检查容器镜像更新,并自动重启容器使用新镜像。

# 每天凌晨3点05分更新
docker run -d \
--name watchtower \
--restart always \
-e TZ=Asia/Shanghai \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
-s "0 5 3 * * *"

注意:Watchtower 会自动更新所有容器。如果只想更新特定容器,可以使用标签过滤功能。

数据库备份和恢复

定期备份数据库是保障数据安全的重要措施。以下脚本实现了自动备份 PostgreSQL 数据库并推送到 Git 仓库。

PostgreSQL 数据库备份

备份脚本:/opt/vps-backup/scripts/backup_database.sh

#!/bin/bash
CONTAINER_NAME="postgres"
BACKUP_DIR="/opt/vps-backup/database"
BACKUP_PREFIX=postgres
BACKUP_POSTFIX=$(date +%Y%m%d_%H)
DATABASES=("memos" "n8n" "umami" "linkding" "artalk")
mkdir -p $BACKUP_DIR
for DB_NAME in "${DATABASES[@]}"
do
BACKUP_FILE="${BACKUP_PREFIX}_${DB_NAME}_${BACKUP_POSTFIX}.sql"
echo "docker exec -it $CONTAINER_NAME pg_dump -U postgres $DB_NAME > $BACKUP_DIR/$BACKUP_FILE"
docker exec -it $CONTAINER_NAME pg_dump -U postgres $DB_NAME > $BACKUP_DIR/$BACKUP_FILE
if [ $? -eq 0 ]; then
echo "DB $DB_NAME backup success: $BACKUP_FILE"
else
echo "DB $DB_NAME backup fail"
fi
done
# 删除超过3天的备份文件
find $BACKUP_DIR -name "${BACKUP_PREFIX}_*.sql" -type f -mtime +3 -exec rm -f {} \;
cd /opt/vps-backup
git pull
git add .
git commit -m "backup database for $BACKUP_POSTFIX"
git push origin main

数据库恢复

恢复数据库备份:

# 恢复 memos 数据库
docker cp /opt/vps-backup/database/postgres_memos_20251111_12.sql postgres:/tmp/
docker exec -it postgres psql -U postgres -d memos -f /tmp/postgres_memos_20251111_12.sql
# 恢复 umami 数据库
docker cp /opt/vps-backup/database/postgres_umami_20251111_12.sql postgres:/tmp/
docker exec -it postgres psql -U postgres -d umami -f /tmp/postgres_umami_20251111_12.sql

N8n 工作流备份

备份脚本:/opt/vps-backup/scripts/backup_n8n.sh

#!/bin/bash
DATE=$(date +%Y%m%d)
BACKUP_DIR="/opt/vps-backup/n8n"
mkdir -p $BACKUP_DIR/$DATE
cd $BACKUP_DIR
docker exec -u node -it n8n n8n export:workflow --backup --output=./$DATE
docker cp n8n://home/node/$DATE ${BACKUP_DIR}
cd ${BACKUP_DIR}/$DATE
for file in `ls`; do
filename=$(cat "$file" | jq -r '.name')
mv "$file" "$filename".json
done
docker exec -u node -it n8n n8n export:credentials --all --output=./credentials.json
docker cp n8n://home/node/credentials.json ${BACKUP_DIR}
# 删除超过3天的备份目录
find $BACKUP_DIR -type d -mtime +3 -exec rm -rf {} \;
cd /opt/vps-backup
git pull
git add .
git commit -m "backup n8n files for $DATE"
git push origin main

定时任务

使用 crontab 设置定时任务,实现自动化运维:

# 编辑 crontab
crontab -e
# 添加以下任务:
# SSL 证书自动续期(每天1点58分)
58 1 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
# 时钟同步(每20分钟)
*/20 * * * * /usr/sbin/ntpdate pool.ntp.org > /dev/null 2>&1
# 数据库备份(每3小时)
0 */3 * * * sh /opt/vps-backup/scripts/backup_database.sh
# N8n 工作流备份(每天凌晨2点)
0 2 * * * sh /opt/vps-backup/scripts/backup_n8n.sh

参考文章