AFFiNE 自托管完整部署

AFFiNE 自托管完整部署
Photo by Rey Seven / Unsplash

前言

AFFiNE 是一款开源的知识管理工具,支持本地优先和云端同步。本文将详细记录在 2GB 内存 VPS 上从零开始部署 AFFiNE 的完整过程,包括所有遇到的问题和解决方案。

测试环境:

  • 系统:Ubuntu 22.04
  • CPU:2 核
  • 内存:2GB RAM
  • 存储:50GB SSD
  • 面板:1Panel

第一步:环境准备

1.1 检查系统资源

# 检查内存
free -h

# 检查磁盘空间
df -h

# 检查 CPU
lscpu

1.2 安装 Docker 和 Docker Compose

如果使用 1Panel 面板,Docker 环境已自动安装。否则需要手动安装:

# 更新软件源
sudo apt update

# 安装 Docker
curl -fsSL https://get.docker.com | bash

# 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker

# 安装 Docker Compose V2
sudo apt install docker-compose-plugin

# 验证安装
docker --version
docker compose version

1.3 配置 Swap(关键步骤)

重要:2GB 物理内存无法稳定运行 AFFiNE,必须添加 Swap。

# 创建 4GB Swap 文件
sudo fallocate -l 4G /swapfile

# 设置权限
sudo chmod 600 /swapfile

# 格式化为 Swap
sudo mkswap /swapfile

# 启用 Swap
sudo swapon /swapfile

# 验证 Swap 已启用
free -h

# 设置开机自动挂载
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 调整 Swap 使用策略(可选,降低对物理内存的压力)
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

第二步:部署 AFFiNE

2.1 方案选择

有两种部署方式:

方案 A:使用 1Panel 应用商店(推荐新手)

  • 优点:一键安装,自动配置
  • 缺点:配置文件修改不便

方案 B:手动 Docker Compose(推荐进阶用户)

  • 优点:完全控制配置
  • 缺点:需要手动维护

本文以方案 B 为例,方案 A 可参考 1Panel 应用商店说明。

2.2 创建项目目录

# 创建项目目录
mkdir -p /opt/affine
cd /opt/affine

# 创建数据目录
mkdir -p data/config data/storage data/postgres data/redis

2.3 创建 docker-compose.yml

nano docker-compose.yml

粘贴以下内容:

version: '3.8'

services:
  affine:
    image: ghcr.io/toeverything/affine-graphql:stable
    container_name: affine_app
    restart: always
    ports:
      - "3010:3010"
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1200M
        reservations:
          cpus: '0.5'
          memory: 800M
    environment:
      - NODE_OPTIONS=--import=./scripts/register.js
      - AFFINE_CONFIG_PATH=/root/.affine/config
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://affine:affine_password@postgres:5432/affine
      - NODE_ENV=production
      - AFFINE_SERVER_PORT=3010
      - AFFINE_SERVER_HOST=0.0.0.0
    volumes:
      - ./data/config:/root/.affine/config
      - ./data/storage:/root/.affine/storage
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - affine_network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3010/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  postgres:
    image: postgres:16-alpine
    container_name: affine_postgres
    restart: always
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=affine
      - POSTGRES_PASSWORD=affine_password
      - POSTGRES_DB=affine
      - PGDATA=/var/lib/postgresql/data/pgdata
    networks:
      - affine_network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U affine -d affine"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: affine_redis
    restart: always
    volumes:
      - ./data/redis:/data
    command: redis-server --appendonly yes
    networks:
      - affine_network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  affine_config:
  affine_storage:
  affine_postgres:
  affine_redis:

networks:
  affine_network:
    driver: bridge

配置说明:

  • cpus: '1.0':限制最多使用 1 个 CPU 核心
  • memory: 1200M:限制最多使用 1.2GB 内存
  • POSTGRES_PASSWORD:建议修改为强密码
  • ports: "3010:3010":映射到宿主机 3010 端口

2.4 启动服务

# 拉取镜像
docker compose pull

# 启动服务(后台运行)
docker compose up -d

# 查看日志
docker compose logs -f

# 等待 2-3 分钟,直到看到 "Server is ready" 提示

2.5 验证部署

# 检查容器状态(所有容器应为 healthy)
docker compose ps

# 测试服务响应
curl http://localhost:3010/api/health

# 应返回:{"status":"ok"}

访问 http://服务器IP:3010 应该能看到 AFFiNE 登录页面。


第三步:配置反向代理(推荐)

3.1 使用 1Panel OpenResty

如果使用 1Panel 面板,推荐使用内置的 OpenResty 反向代理:

  1. 进入 1Panel 面板 → 网站管理
  2. 创建反向代理网站
  3. 填写配置:
    • 域名:affine.your-domain.com
    • 代理地址:http://127.0.0.1:3010
    • 启用 HTTPS(推荐使用 Let's Encrypt 自动证书)

3.2 使用 Nginx(手动配置)

# 安装 Nginx
sudo apt install nginx

# 创建配置文件
sudo nano /etc/nginx/sites-available/affine

粘贴以下配置:

server {
    listen 80;
    server_name affine.your-domain.com;

    # 重定向到 HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name affine.your-domain.com;

    # SSL 证书路径(需要先申请证书)
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # SSL 配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # 日志
    access_log /var/log/nginx/affine_access.log;
    error_log /var/log/nginx/affine_error.log;

    # 反向代理配置
    location / {
        proxy_pass http://127.0.0.1:3010;
        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;
        
        # WebSocket 支持
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # 超时设置
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

启用配置:

# 创建软链接
sudo ln -s /etc/nginx/sites-available/affine /etc/nginx/sites-enabled/

# 测试配置
sudo nginx -t

# 重启 Nginx
sudo systemctl restart nginx

3.3 配置 AFFiNE Base URL(重要)

如果使用域名访问,必须设置 BASE_URL,否则会出现无限重定向问题。

编辑 docker-compose.yml

nano /opt/affine/docker-compose.yml

affine 服务的 environment 部分添加:

environment:
  - AFFINE_SERVER_BASE_URL=https://affine.your-domain.com
  # ... 其他环境变量

重启服务:

cd /opt/affine
docker compose down
docker compose up -d

第四步:解决常见问题

4.1 容器反复重启

症状:

docker compose ps
# 显示容器状态为 "Restarting"

原因: 内存不足导致 OOM Killer 杀掉进程

解决方案:

  1. 确认 Swap 已启用:free -h
  2. 查看系统日志:dmesg | grep -i oom
  3. 增加 Swap 大小或添加资源限制(见步骤 2.3)

4.2 域名访问出现 ERR_TOO_MANY_REDIRECTS

症状: 浏览器报错"重定向次数过多"

原因: 未设置 AFFINE_SERVER_BASE_URL 导致内部生成 http 链接,反向代理强制跳转 https 形成死循环

解决方案:

  1. 按照步骤 3.3 设置 BASE_URL
  2. 清除浏览器缓存
  3. 如果使用 Cloudflare,清除 CDN 缓存

4.3 CPU 使用率过高

症状: CPU 长期占用 80%-100%

原因: Docker 未限制资源,Node.js 进程占满 CPU

解决方案:
docker-compose.yml 中添加资源限制(见步骤 2.3 的 deploy.resources 配置)

4.4 桌面客户端无法登录

症状: 客户端提示"没有权限"或"内容不存在"

原因: AFFiNE 有两种账号模式

  • Local First Account:浏览器版默认使用,无密码
  • Cloud Account:需要邮箱+密码,桌面客户端仅支持此模式

解决方案:

  1. 在浏览器版中创建云账号:
    • 点击右上角头像
    • 选择"设置密码"或"注册云账号"
    • 输入邮箱和密码
  2. 使用新创建的账号密码登录桌面客户端
  3. 确保在浏览器版中至少创建了一个工作区,并开启云端同步

4.5 数据库初始化失败

症状: 日志显示 Migration failedDatabase connection error

解决方案:

# 停止服务
docker compose down

# 删除 PostgreSQL 数据(会丢失所有数据!)
rm -rf ./data/postgres/*

# 重新启动
docker compose up -d

# 查看日志,等待数据库初始化完成
docker compose logs -f postgres

第五步:日常维护

5.1 查看服务状态

cd /opt/affine

# 查看容器状态
docker compose ps

# 查看资源占用
docker stats

# 查看日志
docker compose logs -f affine

5.2 备份数据

# 停止服务
docker compose down

# 备份整个数据目录
tar -czf affine_backup_$(date +%Y%m%d).tar.gz ./data

# 重启服务
docker compose up -d

恢复数据:

# 停止服务
docker compose down

# 解压备份
tar -xzf affine_backup_20241124.tar.gz

# 重启服务
docker compose up -d

5.3 更新 AFFiNE

cd /opt/affine

# 拉取最新镜像
docker compose pull

# 重启服务
docker compose down
docker compose up -d

# 查看日志确认更新成功
docker compose logs -f

5.4 清理磁盘空间

# 清理未使用的 Docker 资源
docker system prune -a --volumes

# 警告:会删除所有未使用的镜像、容器、网络和卷

第六步:性能优化建议

6.1 系统层面

# 调整文件描述符限制
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf

# 优化 TCP 参数
cat >> /etc/sysctl.conf << EOF
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
EOF

sysctl -p

6.2 PostgreSQL 优化

编辑 docker-compose.yml,在 postgres 服务中添加:

postgres:
  command: 
    - postgres
    - -c
    - shared_buffers=256MB
    - -c
    - max_connections=100
    - -c
    - effective_cache_size=512MB

6.3 Redis 优化

redis:
  command: 
    - redis-server
    - --appendonly yes
    - --maxmemory 256mb
    - --maxmemory-policy allkeys-lru

第七步:安全加固

7.1 配置防火墙

# 安装 UFW
sudo apt install ufw

# 允许 SSH
sudo ufw allow 22/tcp

# 允许 HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# 如果直接暴露 AFFiNE 端口(不推荐)
sudo ufw allow 3010/tcp

# 启用防火墙
sudo ufw enable

# 查看规则
sudo ufw status

7.2 定期更新系统

# 更新软件包列表
sudo apt update

# 升级系统
sudo apt upgrade -y

# 自动清理
sudo apt autoremove -y

7.3 配置自动备份

创建备份脚本:

nano /opt/affine/backup.sh

内容:

#!/bin/bash
BACKUP_DIR="/opt/backups"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DIR
cd /opt/affine

docker compose down
tar -czf $BACKUP_DIR/affine_$DATE.tar.gz ./data
docker compose up -d

# 保留最近 7 天的备份
find $BACKUP_DIR -name "affine_*.tar.gz" -mtime +7 -delete

echo "Backup completed: affine_$DATE.tar.gz"

设置权限和定时任务:

chmod +x /opt/affine/backup.sh

# 添加到 crontab(每天凌晨 2 点备份)
crontab -e

# 添加以下行
0 2 * * * /opt/affine/backup.sh >> /var/log/affine_backup.log 2>&1

总结

本文完整记录了在 2GB 内存 VPS 上部署 AFFiNE 的全过程,包括:

  • ✅ 环境准备(Swap 配置)
  • ✅ Docker Compose 部署
  • ✅ 反向代理配置
  • ✅ 常见问题排查
  • ✅ 日常维护方案
  • ✅ 性能优化建议
  • ✅ 安全加固措施

关键要点:

  1. 2GB 内存必须添加 Swap
  2. 必须配置资源限制防止 CPU 占满
  3. 使用域名必须设置 BASE_URL
  4. 桌面客户端需要创建云账号

资源占用情况:

  • CPU:日常 5%-30%,峰值 50%
  • 内存:物理内存 1.5GB,Swap 300MB
  • 磁盘:约 5-10GB(取决于数据量)

如果你的服务器配置更高(如 6 核 8GB),可以移除资源限制配置,获得更好的性能表现。


相关链接: