OneDrive Linux 客户端部署教程(abraunegg/onedrive)

[!NOTE]
本教程基于 2026-08-02 在 AWS aws-sg-t4g(Ubuntu 24.04 ARM64)VPS 上的实际部署过程整理,覆盖编译安装、配置、两种运行方案、定时脚本、推送通知、常见坑与排错。

脱敏说明:文中的服务器 IP 已做隐私处理,统一用 <SERVER_A_IP> / <SERVER_B_IP> 占位表示。

颜色说明■ 红色 = 危险 / 错误■ 橙色 = 警告■ 绿色 = 成功 / 推荐■ 蓝色 = 提示 / 命令


1. 架构总览

┌─────────────────────┐  scp tar.gz  ┌──────────────────────────┐  upload-only  ┌─────────────┐
│ Server A            │ ───────────▶ │ Server B(本教程主角)    │ ────────────▶ │ OneDrive 云端│
│ (数据源机)         │             │ (中转 + 上传端)          │  onedrive CLI │ (ustc.edu)  │
└─────────────────────┘             └──────────────────────────┘               └─────────────┘
  • Server A(<SERVER_A_IP>:数据源。把 /root/data/docker_data 打包成 tar.gz,scp 推送到 Server B
  • Server B(<SERVER_B_IP>ssh aws-sg-t4g:中转 + 上传端。接收备份 → 每来源 IP 保留最新 2 份 → 每天 05:00 定时上传 OneDrive
  • OneDrive 账号根目录结构:Apps/ Attachments/ backup/ npm/ oci-aws/ oci-start-docker/(其中 AppsAttachments 是 Mac 端账号自带的空文件夹)

2. 环境信息

项目
系统Ubuntu 24.04.4 LTS
架构arm64(AWS t4g)
内存1.8 GiB + 2 GiB swap
SSH 别名aws-sg-t4groot@<SERVER_B_IP>
客户端版本onedrive v2.5.11-6-gac3f042(master 源码编译)
同步目录/root/data
白名单backup/npm/oci-aws/oci-start-docker/

3. 客户端编译安装

3.1 安装编译依赖(Debian/Ubuntu ARM64)

sudo apt update
sudo apt install -y build-essential libcurl4-openssl-dev libsqlite3-dev \
    pkg-config git curl systemd-dev libdbus-1-dev ldc

[!WARNING]
ARM64 建议用发行版自带的 ldc 编译器(保证一致性)。编译需要约 1GB 内存 + 1GB swap,内存不足时 ldc2 会被 OOM killer 干掉(本机实测发生过)。

3.2 从源码编译安装

git clone https://github.com/abraunegg/onedrive.git
cd onedrive
./configure
make
sudo make install

验证:

onedrive --version
# onedrive v2.5.11-6-gac3f042

3.3 授权登录

onedrive --auth-uri
  1. 用浏览器打开输出的授权链接,登录微软账号并同意
  2. 把授权后浏览器跳转的完整 URL 粘贴回终端
  3. 成功后生成 ~/.config/onedrive/refresh_token,即授权完成

4. 配置

4.1 主配置 ~/.config/onedrive/config

# 核心配置
sync_dir = "/root/data"
skip_dotfiles = "true"

# 性能优化:匹配你的单核 VPS,防止由于线程过多导致的不稳定
threads = "1"
参数说明
sync_dir本地同步目录。关键:它永远对应 OneDrive 账号根目录,不是云端子目录
skip_dotfiles跳过 . 开头的文件
threads并发线程数,小内存 VPS 建议 1

4.2 sync_list 白名单(推荐)

文件位置:~/.config/onedrive/sync_list

backup/
npm/
oci-aws/
oci-start-docker/
  • 每行一个相对 sync_dir 的路径,目录带 / 结尾
  • 文件存在即生效,不在白名单内的内容完全忽略(例如账号里 Mac 端的 Apps/Attachments/ 不会被同步)
  • 修改 sync_list 后需要跑一次 --resync,否则客户端报 126

4.3 必须理解的概念

[!IMPORTANT]
sync_dir 永远映射 OneDrive 账号根目录:把它指向本地子目录(如 /root/data/backup)并不会"只同步云端 backup/",而是把整个账号镜像进该目录。正确做法是保持 sync_dir = "/root/data",再用 sync_list--single-directory 'backup' 限定范围。

  1. 状态文件(都在 ~/.config/onedrive/):
    • items.sqlite3:本地同步数据库(记录每个文件的状态)
    • .config.hash / .config.backup:配置快照(用于检测配置变更)
    • .sync_list.hash:白名单快照
    • 删数据库时若不同时清掉这几个 hash 快照文件,客户端仍会判定"配置变更 → 要求 resync"
  2. 配置/白名单变更:客户端用退出码 126 表示"需要 --resync"。

5. 常用命令

onedrive --sync                                      # 一次性同步(双向)
onedrive --sync --upload-only                        # 一次性只上传,不下载
onedrive --monitor                                   # 常驻监控(双向、实时)
onedrive --sync --single-directory 'backup' --upload-only  # 只同步 backup/ 目录
onedrive --resync --sync                             # 重建数据库后全量同步(会交互确认)
onedrive --display-config                            # 显示生效配置
onedrive --display-sync-status                       # 查询云端待同步状态
onedrive --version                                   # 版本

[!WARNING]
本版本(v2.5.x)的参数是 --sync不存在 --synchronize。旧文档里的 --synchronize 会直接报 Unrecognized option 并退出,同步不会执行。


6. 方案选择:定时脚本 vs 常驻监控

对比项常驻监控(--monitor定时脚本(cron + --sync --upload-only
运行方式常驻内存,开机自启、崩溃自动重启到点跑一次,跑完退出
同步方向双向,实时只上传,不拉取云端变化
资源占用约 30–190 MB 常驻几乎为零
通知无内建 Telegram 推送可带成功/失败状态推送
失败处理systemd 自动重启等下次触发
适合场景需要频繁双向同步单向备份(本场景)

[!TIP]
本项目选择:定时脚本方案。原因:备份是凌晨生成的一次性大文件,不需要实时双向同步;定时上传 + 推送通知更省资源、更可控。

6.1 若选择常驻监控(参考)

# 安装用户级 systemd 服务
mkdir -p ~/.config/systemd/user
cp /root/onedrive/contrib/systemd/onedrive.service ~/.config/systemd/user/
loginctl enable-linger root
export XDG_RUNTIME_DIR=/run/user/0
systemctl --user daemon-reload
systemctl --user enable --now onedrive

# 停用 / 禁用
systemctl --user stop onedrive
systemctl --user disable onedrive

注意:onedrive@.service(系统级模板)把配置目录硬编码为 /home/%i/.config/onedrive不适用于 root(root 家目录是 /root),所以本项目使用用户级 onedrive.service


7. 定时脚本部署(Server B)

7.1 同步脚本 /root/onedrivebk.sh

#!/bin/bash
# OneDrive 定时备份同步脚本(每天 05:00 由 cron 执行)
LOG="/var/log/onedrivebk.log"
cd /root

echo "===== $(date "+%F %T") sync start =====" >> "$LOG"
/usr/local/bin/onedrive --sync --upload-only >> "$LOG" 2>&1
SYNC_EXIT=$?
echo "===== $(date "+%F %T") sync end (exit=$SYNC_EXIT) =====" >> "$LOG"

if [ "$SYNC_EXIT" -eq 0 ]; then
    MSG="🎉🎉 onedrive 云数据备份成功 🎉🎉"
else
    MSG="☹️☹️ OneDrive 云数据备份失败 (exit=${SYNC_EXIT}) ☹️☹️"
fi

chmod +x /root/tgMessagePush.sh && /root/tgMessagePush.sh "$MSG"
exit $SYNC_EXIT

功能:

  • 增量上传:只上传新增/修改的本地文件,未变的跳过
  • 本地删除的文件会传播到云端(upload-only 也包含删除同步)
  • 日志写入 /var/log/onedrivebk.log,带起止时间和退出码
  • 按退出码推送 🎉🎉 onedrive 云数据备份成功 🎉☹️☹️ OneDrive 云数据备份失败 (exit=126) ☹️☹️ 到手机

7.2 推送脚本 /root/tgMessagePush.sh

支持自定义消息文本(./tgMessagePush.sh "文案"),不传参时使用 config.conf 里的 messageText;同时发 Bark + Telegram,均已正确做 URL 编码(中文/emoji/括号均可发送)。

#!/bin/bash

# 发送 Telegram / Bark 推送通知
# 用法: ./tgMessagePush.sh ["自定义消息文本"]   (不传参数时使用 config.conf 里的 messageText)
# 从配置文件读取 barkApi、telegramBotToken、telegramBotUserId

cd "$(dirname "$0")"
config_file="config.conf"

barkApi=$(awk -F "=" "/barkApi/ {gsub(/^[ \t]+|[ \t]+\$/, \"\", \$2); print \$2}" "$config_file")
configMessage=$(awk -F "=" "/messageText/ {gsub(/^[ \t]+|[ \t]+\$/, \"\", \$2); print \$2}" "$config_file")
telegramBotToken=$(awk -F "=" "/telegramBotToken/ {gsub(/^[ \t]+|[ \t]+\$/, \"\", \$2); print \$2}" "$config_file")
telegramBotUserId=$(awk -F "=" "/telegramBotUserId/ {gsub(/^[ \t]+|[ \t]+\$/, \"\", \$2); print \$2}" "$config_file")

# 优先使用传入参数作为消息文本
messageText="${1:-$configMessage}"

# --- Bark 推送(路径式:/Key/OneDrive/正文,正文做 URL 编码)---
if [ -n "$barkApi" ]; then
    encodedMsg=$(printf "%s" "$messageText" | jq -sRr @uri)
    response=$(curl -sS "https://bark.zszweb.top/${barkApi}/OneDrive/${encodedMsg}?group=OneDrive&autoCopy=1&isArchive=1&sound=shake&level=timeSensitive&icon=https%3A%2F%2Fraw.githubusercontent.com%2FNodewebzsz%2Ftool%2Fmain%2FIconSet%2FColor%2B%2FOneDrive_1.png")
    code=$(printf "%s" "$response" | jq -r ".code // empty" 2>/dev/null)
    if [ "$code" = "200" ]; then
        echo "bark 推送成功"
    else
        echo "bark 推送失败,请检查 bark 机器人 bark_api (response: ${response})"
    fi
else
    echo "未配置 bark 推送"
fi

# --- Telegram 推送 ---
if [ -n "$telegramBotToken" ]; then
    response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "https://api.telegram.org/bot${telegramBotToken}/sendMessage" \
        --data-urlencode "chat_id=${telegramBotUserId}" \
        --data-urlencode "parse_mode=HTML" \
        --data-urlencode "text=${messageText}")
    if [ "$response" = "200" ]; then
        echo "Telegram 推送成功"
    else
        echo "Telegram 推送失败,请检查 Telegram 机器人 token 和 ID"
    fi
else
    echo "未配置 Telegram 推送"
fi

配置文件 /root/config.conf 需要包含以下键(值换成你自己的):

[bark]
barkApi=你的Bark设备Key

[telegram]
telegramBotToken=你的Telegram Bot Token
telegramBotUserId=你的Telegram用户ID

[message]
messageText=默认消息文案

7.3 crontab(每天 05:00)

0 5 * * * /root/onedrivebk.sh >> /var/log/onedrivebk.log 2>&1
# 添加(幂等写法)
(crontab -l 2>/dev/null | grep -v "onedrivebk.sh"; echo "0 5 * * * /root/onedrivebk.sh >> /var/log/onedrivebk.log 2>&1") | crontab -

设计理由:

  • 05:00:避开 Server A 凌晨推送备份的时段,防止两个 onedrive 实例抢数据库锁
  • 输出重定向到日志:cron 默认把输出发到 root 邮箱(基本没人看),落盘方便排查

7.4 logrotate 防止日志无限增长

/etc/logrotate.d/onedrivebk

/var/log/onedrivebk.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    copytruncate
}

每天轮转一次,保留最近 14 份,旧的自动 gzip 压缩。


8. Server A 推送脚本(数据源侧,参考)

被备份机 Server A 上运行的脚本(打包 → 传输 → 远程触发上传):


#!/bin/bash

# --- 配置区 ---
SRC_DIR="/root/data/docker_data"
DST_DIR="/root/data/backup"
SERVER_A_IP="161.118.255.194"
SERVER_B_IP="13.250.172.154"
SSH_USER="root"
SSH_PORT="22"
BACKUP_NAME_PREFIX="docker_data_backup_${SERVER_A_IP}"
ARCHIVE_FORMAT="tar.gz"
SRC_HOST="sg-arm-24g"
LOG_FILE="/var/log/backup.log"

# --- 1. 执行打包 ---
current_date=$(date +"%Y%m%d_%H%M%S")
backup_filename="${BACKUP_NAME_PREFIX}_${current_date}.${ARCHIVE_FORMAT}"

cd $(dirname $SRC_DIR) # 进入父目录以保证打包路径正确
tar -czf "/tmp/${backup_filename}" -C "${SRC_DIR}" .

# 配置免密登录
# ssh-copy-id "${SSH_USER}@${SERVER_B_IP}"


# --- 2. 传输到 B 服务器的/root/data/backup文件夹下---
scp -P "${SSH_PORT}" "/tmp/${backup_filename}" "${SSH_USER}@${SERVER_B_IP}:${DST_DIR}/"
rm -f "/tmp/${backup_filename}"

# --- 3. 远程触发 B 服务器的清理与同步 ---
# 加入防断连
# 注意:这里 tail -n +3 表示保留最新的 2 个文件
ssh -p "${SSH_PORT}" "${SSH_USER}@${SERVER_B_IP}" \
"find ${DST_DIR} -type f -name 'docker_data_backup_*_*.${ARCHIVE_FORMAT}' | \
sed 's/.*docker_data_backup_\([0-9.]*\)_.*/\1/' | \
sort | uniq | \
while read -r ip; do \
    ls -t ${DST_DIR}/docker_data_backup_\"\$ip\"_*.${ARCHIVE_FORMAT} 2>/dev/null | tail -n +3 | xargs --no-run-if-empty rm -f; \
done && onedrive --sync --single-directory 'backup' --upload-only"
rc=$?

# --- 4. 推送完成通知(成功才推送)---
if [ "${rc}" -eq 0 ]; then
    message="✅ [$(date '+%Y-%m-%d %H:%M:%S')] ${SRC_HOST} (${SERVER_A_IP}) 备份完成,数据已同步到 OneDrive 中转服务器 (${SERVER_B_IP})"
    ssh -o ConnectTimeout=20 -o BatchMode=yes "${SSH_USER}@${SERVER_B_IP}" "/root/tgMessagePush.sh \"${message}\"" >> "${LOG_FILE}" 2>&1
else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 备份同步失败,exit=${rc}" >> "${LOG_FILE}"
fi

exit "${rc}"



职责:

  1. 打包 Server A 的 /root/data/docker_data
  2. scp 推送到 Server B 的 /root/data/backup/
  3. 远程在 B 机上:对每个来源 IP 只保留最新 2 个 tar.gz,然后触发 --single-directory 'backup' --upload-only 上传

[!CAUTION]
B 机清理旧备份后,上传同步会把本地删除传播到云端——所以云端每个服务器的备份同样只保留最新 2 份。如需更长的云端保留期,需调整清理逻辑(例如把 tail -n +3 改为更大的数字)。


9. 常见坑与排错

现象原因处理
Unrecognized option --synchronize参数名错误改用 --sync
退出码 126,日志提示 --resync is required配置或 sync_list 变更~/.config/onedrive/.config.hash.config.backupitems.sqlite3 后重跑;或执行 onedrive --resync --sync
两个实例互相报错/抢锁同时跑 monitor 和 cron 脚本错开时间,只保留一个方案
Skipping item - invalid name (Microsoft Naming Convention)文件名含冒号 : 等微软禁用字符正常跳过,本地文件不受影响
inotify_add_watch failed ... Permission deniedDocker 容器用户(uid 1001)目录权限问题已改用 cron 方案规避;如必须监控,需处理目录权限
curl HTTP/2 版本警告libcurl 8.5.0 已知 bug客户端自动降级 HTTP/1.1,无影响
Online file integrity failure上传校验失败客户端自动按 modified 重新上传,无影响
手动删了本地文件,云端文件也消失upload-only 同样传播本地删除符合预期,注意这是单向备份的设计
--resync 交互提示官方安全确认输入 Y 继续(会重建本地数据库)

10. 验证清单

onedrive --version
# onedrive v2.5.11-6-gac3f042

onedrive --display-config
# sync_dir = "/root/data",threads = "1"

cat /root/.config/onedrive/sync_list
# backup/
# npm/
# oci-aws/
# oci-start-docker/

crontab -l
# 0 5 * * * /root/onedrivebk.sh >> /var/log/onedrivebk.log 2>&1

tail -5 /var/log/onedrivebk.log
# ===== 2026-08-02 19:41:00 sync end (exit=0) =====

export XDG_RUNTIME_DIR=/run/user/0
systemctl --user status onedrive
# inactive / disabled(监控服务已停用,不冲突)

手动跑一次完整流程:

cd /root && ./onedrivebk.sh

预期:Bark 推送成功 + Telegram 推送成功,手机收到 🎉🎉 onedrive 云数据备份成功 🎉🎉


11. 安全注意事项

[!WARNING]
refresh_token、Telegram Bot Token、Bark Key 都是敏感凭据,严禁外传或进入代码库。

  • ~/.config/onedrive/refresh_token 等同于账号凭据,权限保持 600不要外传、不要进代码库
  • /root/config.conf 中的 Telegram Bot Token、Bark Key 同样敏感
  • 不要把任何云端凭据放进前端代码或 NEXT_PUBLIC_ 环境变量;需要管理员权限的操作必须在服务端完成
  • 修改任何脚本前先 cp 备份,避免操作失误影响正在运行的定时任务

附:本部署的时间线(供排查参考)

时间(CST)事件
15:17–15:19系统重启,Docker 容器恢复,oci-start/watchtower 停止
15:22–15:23ldc2(D 编译器)编译时 OOM 被内核杀死
17:03onedrive v2.5.11 编译完成
17:13 / 17:28配置创建 / 授权完成(refresh_token)
17:40首次 --sync,下载 4 个云端备份
17:47sync_dir 被改为 /root/data/backup(触发后续 resync 风波)
17:50错误方向的 --resync,把整个账号镜像进 backup/
17:54监控服务启动,出现 EACCES 与误删
18:00sync_dir 改回 /root/data
19:00–19:40清理现场、重建数据库、加 sync_list、配置 crontab、logrotate、状态推送