Joplin 服务器最终版部署 SOP

这是一个基于我们之前排查过程，为您量身定制的 **Joplin 服务器最终版部署 SOP**。

这份文档跳过了所有试错步骤，直接采用**最稳定、成功率最高**的配置方案（硬编码配置，避免环境变量错误）。您可以将此文档保存到您的知识库中，未来重装或迁移时直接照做即可。

-----

# 甲骨文云 Joplin Server 部署标准作业程序 (SOP)

**最后更新时间：** 2025-11-30
**适用环境：** Oracle Cloud (Ubuntu 24.04), Docker 已安装
**目标域名：** `joplin.078919.xyz`

-----

## 第一阶段：云端防火墙配置 (Oracle Security List)

在操作服务器终端之前，必须先确保甲骨文云的“大门”是打开的。

1.  登录 **Oracle Cloud 网页控制台**。
2.  路径：`Compute Instance` -\> `VCN` -\> `Subnet` -\> `Security Lists`。
3.  点击当前的 Security List，查看 **Ingress Rules (入站规则)**。
4.  确保存在以下规则（如果缺失请点击 "Add Ingress Rules" 添加）：
      * **Source CIDR:** `0.0.0.0/0`
      * **Protocol:** TCP
      * **Destination Port Range:** `80,443`
      * *(可选: 如果您还需要访问 Portainer，请确保 9443 和 8000 也在列表里)*

-----

## 第二阶段：服务器环境清理

SSH 登录到服务器，确保没有旧的防火墙规则干扰。

```bash
# 切换到 root (可选，或在命令前加 sudo)
sudo -i

# 1. 确保 iptables 处于“开放/裸奔”状态 (Oracle Ubuntu 默认规则可能会阻挡 Docker)
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
netfilter-persistent save

# 2. (如果是重装) 清理旧的 Joplin 容器和残留数据
cd /home/ubuntu/joplin 2>/dev/null && docker compose down
rm -rf /home/ubuntu/joplin
```

-----

## 第三阶段：部署核心配置文件

我们将使用 **Docker Compose** 编排三个容器：Joplin (应用), Postgres (数据库), Caddy (自动HTTPS网关)。

### 1\. 创建工作目录

```bash
mkdir -p /home/ubuntu/joplin
cd /home/ubuntu/joplin
```

### 2\. 创建 Caddyfile (处理域名和 HTTPS)

请直接复制以下代码块并在终端执行：

```bash
cat <<EOF > Caddyfile
joplin.078919.xyz {
    reverse_proxy app:22300
}
EOF
```

### 3\. 创建 docker-compose.yml (核心配置)

*注意：为了防止之前遇到的“特殊字符报错”，这里直接将密码写入配置文件。*

```bash
cat <<EOF > docker-compose.yml
services:
  db:
    image: postgres:16
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    restart: unless-stopped
    environment:
      - POSTGRES_PASSWORD=mtjjtmp1
      - POSTGRES_USER=joplin
      - POSTGRES_DB=joplin

  app:
    image: joplin/server:latest
    depends_on:
      - db
    ports:
      - "22300:22300"
    restart: unless-stopped
    environment:
      - APP_PORT=22300
      - APP_BASE_URL=https://joplin.078919.xyz
      - DB_CLIENT=pg
      - POSTGRES_PASSWORD=mtjjtmp1
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=joplin
      - POSTGRES_PORT=5432
      - POSTGRES_HOST=db
      - MAILER_ENABLED=0

  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./data/caddy_data:/data
      - ./data/caddy_config:/config
    depends_on:
      - app
EOF
```

-----

## 第四阶段：启动与验证

### 1\. 启动服务

```bash
docker compose up -d
```

  * *初次运行会自动下载镜像，如果下载卡住不动（超过10分钟），请按 `Ctrl+C` 中断后重新运行该命令。*

### 2\. 检查状态

等待约 30 秒后，执行：

```bash
docker compose ps
```

  * **成功标准：** 所有容器 (joplin-app, joplin-db, joplin-caddy) 的状态都显示为 **Up**。
  * *如果 `db` 显示 `Restarting`，通常需要执行 `docker compose down` 然后 `rm -rf data` 再重新启动。*

-----

## 第五阶段：Web 初始化与客户端设置

### 1\. 网页端初始化

1.  打开浏览器访问：`https://joplin.078919.xyz`
2.  **默认登录凭据：**
      * Email: `admin@localhost`
      * Password: `admin`
3.  **立刻修改：** 登录后系统会强制要求你修改邮箱和密码，请务必修改并牢记。

### 2\. 客户端设置 (电脑/手机)

1.  打开 Joplin 软件 -\> **工具/设置** -\> **同步**。
2.  **同步目标：** 选择 `Joplin 服务器 Beta`。
3.  **服务器 URL：** 填入 `https://joplin.078919.xyz`。
4.  **用户名/密码：** 填入你刚才在网页端修改后的新账号和密码。
5.  点击“**检查同步配置**”，显示“成功”即可。

6. Joplin管理员账户及密码 admin@localhost
@Mtjjtmp1
Joplin同步账户及密码
kl_huang@yahoo.com
@Mtjjtmp1
-----
## 给服务器增加“虚拟内存（Swap）”，解决内存溢出问题

这是一个非常典型的\*\*内存溢出（Out Of Memory, OOM）\*\*现象，特别常见于甲骨文云的免费实例（特别是只有 1GB 内存的 AMD 实例）。

### 这里的原理是：

1.  **平常状态：** Joplin Server、Postgres 数据库和 Caddy 勉强在 1GB 内存里共存，相安无事。
2.  **同步时刻：** 客户端开始大量上传/下载数据，Joplin Server 开始拼命计算，Postgres 数据库开始疯狂读写索引。
3.  **崩溃点：** 内存瞬间耗尽。Linux 系统为了保护自己不彻底死机，触发了 \*\*OOM Killer（内存杀手）\*\*机制，它会优先杀死占用内存最大的进程（通常是 Postgres 数据库），或者导致系统因频繁交换数据（Swap thrashing）而卡死，表现就是 **SSH 断联，网站打不开**。

**只要给服务器增加“虚拟内存（Swap）”，这个问题就能 100% 解决。** 甲骨文的 Ubuntu 默认往往没有配置 Swap，或者配置得很小。

请按照以下 SOP 为你的服务器“扩容”。

-----

### 解决步骤：增加 4GB 虚拟内存 (Swap)

既然 SSH 现在能连上了，请立刻执行以下操作。这相当于给你的服务器增加了一个“备用蓄水池”，当物理内存不够时，系统会把不常用的数据暂时存到硬盘上，从而保证系统不卡死。

#### 第 1 步：检查当前内存情况

```bash
free -h
```

*如果 `Swap:` 这一行显示全是 `0B`，说明你之前确实是在“走钢丝”。*

#### 第 2 步：创建 Swap 文件 (一键执行)

请依次复制并执行以下命令：

1.  **创建一个 4GB 的文件：**

    ```bash
    sudo fallocate -l 4G /swapfile
    ```

    *(如果没有 fallocate 命令，可以用 `sudo dd if=/dev/zero of=/swapfile bs=1M count=4096` 代替)*

2.  **设置权限（安全要求）：**

    ```bash
    sudo chmod 600 /swapfile
    ```

3.  **把它格式化为 Swap 区域：**

    ```bash
    sudo mkswap /swapfile
    ```

4.  **启用 Swap：**

    ```bash
    sudo swapon /swapfile
    ```

#### 第 3 步：设置开机自动挂载 (永久生效)

为了防止重启后 Swap 消失，我们需要把它写入系统配置。

```bash
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```

#### 第 4 步：调整系统倾向性 (优化)

告诉 Linux 系统：**“尽量多用物理内存，实在不行了再用 Swap”**，这样可以保证性能。

```bash
# 临时生效
sudo sysctl vm.swappiness=10

# 永久生效
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
```

-----

### 第 5 步：验证与重启服务

1.  **再次检查内存：**

    ```bash
    free -h
    ```

    现在你应该能看到 `Swap:` 后面有 `4.0Gi` 了。

2.  **重启 Docker 服务（让容器感知到内存充裕）：**

    ```bash
    sudo systemctl restart docker
    ```

-----

### 再次尝试同步

现在，你可以放心地打开 Windows 或 Android 客户端的同步开关了。
即使同步过程中内存吃紧，系统也只会稍微变慢一点点（因为用到了硬盘读写），而绝对不会再出现断联、死机的情况。

**这个操作是甲骨文云小内存机器的“必做”优化。** 完成后请告诉我结果！

## 附录：维护与故障排除

  * **重启服务器后：**
    无需任何操作，Joplin 会自动随系统启动。

  * **查看日志 (排错用)：**

    ```bash
    cd /home/ubuntu/joplin
    docker compose logs -f
    # 或只看某个服务
    docker compose logs -f caddy
    ```

  * **数据备份：**
    所有的笔记数据都在 `/home/ubuntu/joplin/data` 目录下。建议定期备份此目录。

  * **更新版本：**

    ```bash
    cd /home/ubuntu/joplin
    docker compose pull
    docker compose up -d
    ```

id: 23395d428f8a41c4839b8630838fec3c
parent_id: c8972912af904d98b5610c325e3d5ad3
created_time: 2025-11-30T05:59:32.654Z
updated_time: 2025-11-30T12:51:07.147Z
is_conflict: 0
latitude: 25.03296940
longitude: 121.56541770
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 0
user_created_time: 2025-11-30T05:59:32.654Z
user_updated_time: 2025-11-30T12:51:07.147Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1