Published on February 11, 2026 by Dominic Böttger · 16 min read
If you’ve switched to Arch Linux but still need access to SharePoint document libraries from work, you’ve probably noticed that Microsoft doesn’t offer a native OneDrive client for Linux. On Windows, clicking the “Sync” button in SharePoint just works. On Linux, nothing happens.
In this guide, I’ll show you how to make that “Sync” button work on Arch Linux - complete with automatic drive detection, subfolder sync, Windows-style folder naming, and systemd background services. Click “Sync” in your browser, and the files appear in ~/SharePoint/.
What We’re Building
When you click “Sync” on a SharePoint library or folder in your browser, SharePoint fires an odopen:// protocol URL. We’ll set up:
- abraunegg/onedrive - the open-source OneDrive sync client for Linux
- A protocol handler (
odopen-handler.sh) that catchesodopen://URLs from the browser - XDG desktop integration so the system routes these URLs to our handler
- Systemd user services for persistent background sync
The end result: click “Sync” in SharePoint, a terminal opens for first-time setup, and then your files sync in the background - just like on Windows.
Prerequisites
- Arch Linux (or an Arch-based distro)
- An AUR helper like
yayorparu python3(for URL decoding)notify-send(usually included with your DE/WM)- A terminal emulator (alacritty, kitty, foot, etc.)
Step 1: Install abraunegg/onedrive
Install the OneDrive client from the AUR:
yay -S onedrive-abrauneggThis builds the client from source (it’s written in D, so it will pull in dmd as a build dependency).
Verify the installation:
onedrive --version
# onedrive v2.5.10Step 2: Authenticate with Microsoft
You need to authenticate once so the client can obtain a refresh token. If you don’t need your entire personal OneDrive synced locally, you can skip the full sync and just do a one-time authentication.
Run onedrive and immediately cancel after authenticating:
onedrive --sync --verboseThis will print a URL - open it in your browser and sign in with your Microsoft account. Once the authentication completes, press Ctrl+C to stop the sync. The refresh token is now saved at ~/.config/onedrive/refresh_token.
If you don’t want any personal OneDrive files locally, remove the default sync directory:
rm -rf ~/OneDriveThe default OneDrive service is disabled by default - it won’t sync in the background unless you explicitly enable it:
# Verify it's not running (it shouldn't be)
systemctl --user status onedriveStep 3: Install the Protocol Handler Script
Save the following script to ~/.local/bin/odopen-handler.sh:
#!/bin/bash
# =============================================================================
# odopen-handler.sh
# Protocol handler for odopen:// URLs (Teams/SharePoint "Sync" button)
# Uses abraunegg/onedrive as sync backend
# =============================================================================
set -euo pipefail
LOGFILE="$HOME/.local/share/odopen-handler/handler.log"
BASE_SYNC_DIR="$HOME/SharePoint"
BASE_CONFIG_DIR="$HOME/.config/onedrive-sharepoint"
mkdir -p "$(dirname "$LOGFILE")"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}
# ---------------------------------------------------------------------------
# URL parsing helpers
# ---------------------------------------------------------------------------
urldecode() {
python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))" "$1"
}
parse_param() {
local value
value=$(echo "$1" | grep -oP "${2}=\K[^&]+" | head -1 || true)
if [[ -n "$value" ]]; then
urldecode "$value"
fi
}
# ---------------------------------------------------------------------------
# Notification helper (works with most DEs)
# ---------------------------------------------------------------------------
notify() {
if command -v notify-send &>/dev/null; then
notify-send -i folder-sync "OneDrive Sync" "$1"
fi
log "$1"
}
# ---------------------------------------------------------------------------
# --list: Show all active syncs
# ---------------------------------------------------------------------------
cmd_list() {
local found=0
for config_dir in "$BASE_CONFIG_DIR"/*/; do
[[ -d "$config_dir" ]] || continue
local name
name=$(basename "$config_dir")
local service_name="onedrive-sharepoint-${name}"
local sync_dir=""
local drive_id=""
local status="stopped"
if [[ -f "$config_dir/config" ]]; then
sync_dir=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$config_dir/config" || true)
drive_id=$(grep -oP 'drive_id\s*=\s*"\K[^"]+' "$config_dir/config" || true)
fi
if systemctl --user is-active "$service_name" &>/dev/null; then
status="running"
elif systemctl --user is-enabled "$service_name" &>/dev/null; then
status="enabled (stopped)"
fi
if [[ $found -eq 0 ]]; then
echo "Active OneDrive SharePoint syncs:"
echo ""
fi
found=$((found + 1))
echo " [$found] $name"
echo " Sync dir: $sync_dir"
echo " Drive ID: ${drive_id:-(not set)}"
echo " Service: $service_name ($status)"
echo ""
done
if [[ $found -eq 0 ]]; then
echo "No SharePoint syncs configured."
fi
}
# ---------------------------------------------------------------------------
# --unsync: Remove a sync configuration
# ---------------------------------------------------------------------------
cmd_unsync() {
local target="${1:-}"
# Collect available syncs
local names=()
for config_dir in "$BASE_CONFIG_DIR"/*/; do
[[ -d "$config_dir" ]] || continue
names+=("$(basename "$config_dir")")
done
if [[ ${#names[@]} -eq 0 ]]; then
echo "No SharePoint syncs configured."
exit 0
fi
# If no target given, show interactive picker
if [[ -z "$target" ]]; then
echo "Which sync do you want to remove?"
echo ""
for i in "${!names[@]}"; do
local n="${names[$i]}"
local sd=""
if [[ -f "$BASE_CONFIG_DIR/$n/config" ]]; then
sd=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$n/config" || true)
fi
local svc_status="stopped"
if systemctl --user is-active "onedrive-sharepoint-${n}" &>/dev/null; then
svc_status="running"
fi
echo " [$((i + 1))] $n ($svc_status)"
[[ -n "$sd" ]] && echo " $sd"
done
echo ""
read -rp "Enter number (or 'q' to quit): " choice
[[ "$choice" == "q" || -z "$choice" ]] && exit 0
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 1 ]] || [[ "$choice" -gt ${#names[@]} ]]; then
echo "Invalid selection."
exit 1
fi
target="${names[$((choice - 1))]}"
fi
# Resolve target: allow passing display name, safe name, or sync dir name
local safe_target=""
for n in "${names[@]}"; do
local sd=""
if [[ -f "$BASE_CONFIG_DIR/$n/config" ]]; then
sd=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$n/config" || true)
fi
# Match against: safe name, sync dir basename, or sync dir full path
if [[ "$n" == "$target" ]] || \
[[ "$(basename "$sd")" == "$target" ]] || \
[[ "$sd" == "$target" ]]; then
safe_target="$n"
break
fi
done
if [[ -z "$safe_target" ]]; then
echo "Error: No sync found for '$target'"
echo "Use --list to see available syncs."
exit 1
fi
local service_name="onedrive-sharepoint-${safe_target}"
local sync_dir=""
if [[ -f "$BASE_CONFIG_DIR/$safe_target/config" ]]; then
sync_dir=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$safe_target/config" || true)
fi
echo ""
echo "Removing sync: $safe_target"
echo " Config: $BASE_CONFIG_DIR/$safe_target"
echo " Sync dir: $sync_dir"
echo " Service: $service_name"
echo ""
read -rp "Delete local files too? [y/N]: " delete_files
# Stop and disable service
systemctl --user stop "$service_name" 2>/dev/null || true
systemctl --user disable "$service_name" 2>/dev/null || true
rm -f "$HOME/.config/systemd/user/${service_name}.service"
systemctl --user daemon-reload
# Remove config
rm -rf "$BASE_CONFIG_DIR/$safe_target"
# Remove local files if requested
if [[ "$delete_files" =~ ^[yY]$ ]] && [[ -n "$sync_dir" ]]; then
rm -rf "$sync_dir"
echo "Removed local files: $sync_dir"
fi
echo "Sync '$safe_target' removed."
log "Unsynced: $safe_target"
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
case "${1:-}" in
--list)
cmd_list
exit 0
;;
--unsync)
cmd_unsync "${2:-}"
exit 0
;;
--help|-h)
echo "Usage:"
echo " odopen-handler.sh <odopen://...> Handle SharePoint sync URL"
echo " odopen-handler.sh --list List all active syncs"
echo " odopen-handler.sh --unsync [name] Remove a sync (interactive if no name)"
echo " odopen-handler.sh --help Show this help"
exit 0
;;
esac
URI="${1:-}"
if [[ -z "$URI" ]]; then
log "ERROR: No URI provided."
notify "Error: No odopen:// URL provided."
exit 1
fi
log "Received URI: $URI"
# --- Parse parameters from odopen:// URL ---
SITE_URL=$(parse_param "$URI" "webUrl")
FOLDER_URL=$(parse_param "$URI" "folderUrl")
FOLDER_NAME=$(parse_param "$URI" "folderName")
LIST_TITLE=$(parse_param "$URI" "listTitle")
WEB_TITLE=$(parse_param "$URI" "webTitle")
SITE_ID=$(parse_param "$URI" "siteId")
LIST_ID=$(parse_param "$URI" "listId")
WEB_ID=$(parse_param "$URI" "webId")
# Strip curly braces (SharePoint sometimes returns IDs with {})
SITE_ID="${SITE_ID//[\{\}]/}"
LIST_ID="${LIST_ID//[\{\}]/}"
WEB_ID="${WEB_ID//[\{\}]/}"
# --- Derive the relative folder path within the library ---
RELATIVE_FOLDER=""
if [[ -n "$FOLDER_URL" ]] && [[ -n "$SITE_URL" ]]; then
LIB_AND_FOLDER="${FOLDER_URL#"$SITE_URL"}"
LIB_AND_FOLDER="${LIB_AND_FOLDER#/}"
RELATIVE_FOLDER="${LIB_AND_FOLDER#*/}"
if [[ "$RELATIVE_FOLDER" == "$LIB_AND_FOLDER" ]]; then
RELATIVE_FOLDER=""
fi
fi
log "Parsed: site=$WEB_TITLE | library=$LIST_TITLE | folder=$FOLDER_NAME | relative_path=$RELATIVE_FOLDER"
log "Site URL: $SITE_URL"
log "Site ID: $SITE_ID | Web ID: $WEB_ID | List ID: $LIST_ID"
# --- Derive unique names - Windows-style "Sitename - Folder" ---
if [[ -n "$FOLDER_NAME" ]] && [[ -n "$RELATIVE_FOLDER" ]]; then
DISPLAY_NAME="${WEB_TITLE} - ${FOLDER_NAME}"
SAFE_NAME=$(echo "${WEB_TITLE}_${FOLDER_NAME}" | tr ' ' '_' | tr -cd '[:alnum:]_-')
SINGLE_DIR="$RELATIVE_FOLDER"
else
DISPLAY_NAME="${WEB_TITLE} - ${LIST_TITLE}"
SAFE_NAME=$(echo "${WEB_TITLE}_${LIST_TITLE}" | tr ' ' '_' | tr -cd '[:alnum:]_-')
SINGLE_DIR=""
fi
CONFIG_DIR="${BASE_CONFIG_DIR}/${SAFE_NAME}"
SYNC_DIR="${BASE_SYNC_DIR}/${DISPLAY_NAME}"
SERVICE_NAME="onedrive-sharepoint-${SAFE_NAME}"
log "Config dir: $CONFIG_DIR"
log "Sync dir: $SYNC_DIR"
log "Single dir: $SINGLE_DIR"
# --- Check if abraunegg/onedrive is installed ---
if ! command -v onedrive &>/dev/null; then
notify "Error: abraunegg/onedrive is not installed!\nInstall with: yay -S onedrive-abraunegg"
log "ERROR: onedrive not found in PATH"
exit 1
fi
# --- Check if this library is already configured ---
if [[ -d "$CONFIG_DIR" ]] && [[ -f "$CONFIG_DIR/config" ]]; then
notify "Sync for '$DISPLAY_NAME' is already configured.\nSync directory: $SYNC_DIR"
if systemctl --user is-active "$SERVICE_NAME" &>/dev/null; then
log "Service $SERVICE_NAME is already running."
notify "Sync is already running in the background."
else
log "Starting existing service $SERVICE_NAME..."
systemctl --user start "$SERVICE_NAME" || true
notify "Sync has been restarted."
fi
exit 0
fi
# --- Create new configuration ---
mkdir -p "$CONFIG_DIR"
mkdir -p "$SYNC_DIR"
log "Creating new onedrive config in $CONFIG_DIR"
# Create config without drive_id (will be determined in the first-run script)
cat > "$CONFIG_DIR/config" <<EOF
# OneDrive SharePoint Sync - $DISPLAY_NAME
# Auto-generated by odopen-handler.sh on $(date)
sync_dir = "$SYNC_DIR"
EOF
# Check if a default OneDrive profile exists (for token reuse)
DEFAULT_CONFIG="$HOME/.config/onedrive"
if [[ -f "$DEFAULT_CONFIG/refresh_token" ]]; then
log "Found existing OneDrive config, copying refresh_token..."
cp "$DEFAULT_CONFIG/refresh_token" "$CONFIG_DIR/refresh_token" 2>/dev/null || true
notify "Configuration created for: $DISPLAY_NAME\n\nDetermining Drive ID..."
else
log "No existing OneDrive config found. First-run will require authentication."
notify "New configuration created.\n\nAuthentication and Drive ID lookup in terminal..."
fi
# --- Build single-directory flags for onedrive CLI ---
SINGLE_DIR_FLAG=""
if [[ -n "$SINGLE_DIR" ]]; then
SINGLE_DIR_FLAG="--single-directory '$SINGLE_DIR'"
fi
# --- Create systemd user service ---
SERVICE_FILE="$HOME/.config/systemd/user/${SERVICE_NAME}.service"
mkdir -p "$(dirname "$SERVICE_FILE")"
EXEC_CMD="/usr/bin/onedrive --monitor --confdir=\"$CONFIG_DIR\""
if [[ -n "$SINGLE_DIR" ]]; then
EXEC_CMD="$EXEC_CMD --single-directory '$SINGLE_DIR'"
fi
cat > "$SERVICE_FILE" <<EOF
[Unit]
Description=OneDrive SharePoint Sync - $DISPLAY_NAME
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=$EXEC_CMD
Restart=on-failure
RestartSec=30
# Performance & Safety
Nice=10
IOSchedulingClass=idle
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
log "Created systemd service: $SERVICE_NAME"
# --- Open interactive terminal for first-run setup ---
# Write env file (avoids sed issues with special chars like & in site names)
FIRST_RUN_ENV=$(mktemp /tmp/odopen-firstrun-XXXXXX.env)
cat > "$FIRST_RUN_ENV" <<ENVEOF
CONFIG_DIR=$(printf '%q' "$CONFIG_DIR")
SERVICE_NAME=$(printf '%q' "$SERVICE_NAME")
WEB_TITLE=$(printf '%q' "$WEB_TITLE")
LIST_TITLE=$(printf '%q' "$LIST_TITLE")
DISPLAY_NAME=$(printf '%q' "$DISPLAY_NAME")
SYNC_DIR=$(printf '%q' "$SYNC_DIR")
SINGLE_DIR=$(printf '%q' "$SINGLE_DIR")
ENVEOF
FIRST_RUN_SCRIPT=$(mktemp /tmp/odopen-firstrun-XXXXXX.sh)
cat > "$FIRST_RUN_SCRIPT" <<'FIRSTRUN'
#!/bin/bash
# Source environment variables
ENV_FILE="${BASH_SOURCE[0]%.sh}.env"
if [[ -f "$ENV_FILE" ]]; then
source "$ENV_FILE"
else
echo "ERROR: Environment file not found: $ENV_FILE"
read
exit 1
fi
echo "======================================================="
echo " OneDrive SharePoint Sync - First-Run Setup"
echo "======================================================="
echo ""
echo " Team/Site: $WEB_TITLE"
echo " Library: $LIST_TITLE"
if [[ -n "$SINGLE_DIR" ]]; then
echo " Folder: $SINGLE_DIR"
fi
echo " Sync dir: $SYNC_DIR"
echo " Config: $CONFIG_DIR"
echo ""
# --- Step 1: Determine the Drive ID for this SharePoint library ---
echo "-------------------------------------------------------"
echo " Step 1: Determining Drive ID for '$WEB_TITLE'..."
echo " (First use: browser window will open for login)"
echo "-------------------------------------------------------"
echo ""
DRIVE_OUTPUT=$(onedrive --get-sharepoint-drive-id "$WEB_TITLE" 2>&1)
echo "$DRIVE_OUTPUT"
echo ""
if echo "$DRIVE_OUTPUT" | grep -q "ERROR"; then
echo "======================================================="
echo " ERROR: SharePoint site '$WEB_TITLE' not found."
echo " Check the site name and your permissions."
echo "======================================================="
echo ""
echo "Press Enter to close..."
read
exit 1
fi
# Extract drive_id - try exact match first, then auto-select if only one result
DRIVE_ID=$(echo "$DRIVE_OUTPUT" | grep -B1 "Library Name: *$LIST_TITLE" | grep -oP 'drive_id:\s+\K\S+' || true)
if [[ -z "$DRIVE_ID" ]]; then
DRIVE_ID_COUNT=$(echo "$DRIVE_OUTPUT" | grep -c 'drive_id:' || true)
if [[ "$DRIVE_ID_COUNT" -eq 1 ]]; then
DRIVE_ID=$(echo "$DRIVE_OUTPUT" | grep -oP 'drive_id:\s+\K\S+')
MATCHED_LIB=$(echo "$DRIVE_OUTPUT" | grep -oP 'Library Name:\s+\K.*')
echo " Auto-selected: '$MATCHED_LIB' (only library found)"
fi
fi
if [[ -z "$DRIVE_ID" ]]; then
echo "-------------------------------------------------------"
echo " Could not auto-match library '$LIST_TITLE'."
echo " Available Drive IDs are shown above."
echo ""
echo " Please enter the drive_id (e.g. b!xxxxxx):"
echo "-------------------------------------------------------"
read -r DRIVE_ID
if [[ -z "$DRIVE_ID" ]]; then
echo "No Drive ID entered. Aborting."
echo "Press Enter to close..."
read
exit 1
fi
fi
echo ""
echo " Found Drive ID: $DRIVE_ID"
echo ""
# --- Step 2: Update config with drive_id ---
echo "-------------------------------------------------------"
echo " Step 2: Updating configuration..."
echo "-------------------------------------------------------"
cat > "$CONFIG_DIR/config" <<EOF2
# OneDrive SharePoint Sync - $DISPLAY_NAME
# Auto-generated by odopen-handler.sh
sync_dir = "$SYNC_DIR"
drive_id = "$DRIVE_ID"
EOF2
echo " Config written: $CONFIG_DIR/config"
echo ""
# --- Step 3: Initial synchronization ---
echo "-------------------------------------------------------"
echo " Step 3: Starting initial sync..."
if [[ -n "$SINGLE_DIR" ]]; then
echo " Folder: $SINGLE_DIR"
fi
echo "-------------------------------------------------------"
echo ""
SYNC_CMD=(onedrive --confdir="$CONFIG_DIR" --sync --verbose --resync --resync-auth)
if [[ -n "$SINGLE_DIR" ]]; then
SYNC_CMD+=(--single-directory "$SINGLE_DIR")
fi
"${SYNC_CMD[@]}"
if [[ $? -eq 0 ]]; then
echo ""
echo "======================================================="
echo " Initial sync completed successfully!"
echo " Enabling background sync..."
echo "======================================================="
systemctl --user enable "$SERVICE_NAME"
systemctl --user start "$SERVICE_NAME"
echo ""
echo " Service status:"
systemctl --user status "$SERVICE_NAME" --no-pager || true
echo ""
echo " Your files are at: $SYNC_DIR"
echo ""
echo " Useful commands:"
echo " Status: systemctl --user status $SERVICE_NAME"
echo " Stop: systemctl --user stop $SERVICE_NAME"
echo " Logs: journalctl --user -u $SERVICE_NAME -f"
echo ""
else
echo ""
echo "======================================================="
echo " ERROR during synchronization."
echo " Check the output above for details."
echo ""
echo " Manual setup:"
echo " onedrive --confdir='$CONFIG_DIR' --sync --verbose"
echo "======================================================="
fi
echo "Press Enter to close..."
read
FIRSTRUN
# Rename env file to match the script name so the script can find it
mv "$FIRST_RUN_ENV" "${FIRST_RUN_SCRIPT%.sh}.env"
chmod +x "$FIRST_RUN_SCRIPT"
# Open terminal - try available terminal emulators
open_terminal() {
local cmd="$1"
if command -v kitty &>/dev/null; then
kitty --title "OneDrive Sync Setup" bash "$cmd" &
elif command -v alacritty &>/dev/null; then
alacritty --title "OneDrive Sync Setup" -e bash "$cmd" &
elif command -v foot &>/dev/null; then
foot --title "OneDrive Sync Setup" bash "$cmd" &
elif command -v gnome-terminal &>/dev/null; then
gnome-terminal --title "OneDrive Sync Setup" -- bash "$cmd" &
elif command -v konsole &>/dev/null; then
konsole -e bash "$cmd" &
elif command -v xterm &>/dev/null; then
xterm -title "OneDrive Sync Setup" -e bash "$cmd" &
else
log "ERROR: No terminal emulator found"
notify "No terminal found!\nPlease run manually:\nbash $cmd"
return 1
fi
}
open_terminal "$FIRST_RUN_SCRIPT"
log "Opened terminal for first-run setup."Make it executable:
chmod +x ~/.local/bin/odopen-handler.shStep 4: Register the odopen:// Protocol Handler
Create a desktop entry so the system knows how to handle odopen:// URLs:
cat > ~/.local/share/applications/odopen-handler.desktop <<EOF
[Desktop Entry]
Type=Application
Name=OneDrive Sync Handler
Comment=Protocol handler for odopen:// URLs (Teams/SharePoint Sync)
Exec=$HOME/.local/bin/odopen-handler.sh %u
MimeType=x-scheme-handler/odopen;
NoDisplay=true
Terminal=false
EOFRegister it with the system:
xdg-mime default odopen-handler.desktop x-scheme-handler/odopen
update-desktop-database ~/.local/share/applications/Verify the registration:
xdg-mime query default x-scheme-handler/odopen
# Should output: odopen-handler.desktopStep 5: Configure Your Browser
Your browser needs to allow odopen:// URLs to open external applications:
- Firefox: When you first click a SharePoint “Sync” button, Firefox will prompt you to select an application. Choose the handler and check “Remember this choice”.
- Chromium/Chrome/Edge: You may need to allow the protocol in
chrome://settings/handlersor accept the prompt when it first appears.
Usage
Syncing a SharePoint Library or Folder
- Navigate to a SharePoint document library or folder in your browser
- Click the “Sync” button
- Your browser will launch the protocol handler
- A terminal window opens showing the first-run setup:
- Step 1: Queries the SharePoint API for the Drive ID
- Step 2: Writes the configuration
- Step 3: Performs the initial sync
- After the first sync completes, a systemd service starts monitoring for changes
Your files will appear in ~/SharePoint/ using Windows-style naming:
| What you sync | Local folder |
|---|---|
| Entire library on “Project Alpha” | ~/SharePoint/Project Alpha - Documents/ |
| Subfolder “Reports” on “Project Alpha” | ~/SharePoint/Project Alpha - Reports/ |
Listing Active Syncs
odopen-handler.sh --listOutput:
Active OneDrive SharePoint syncs:
[1] ProjectAlpha_Reports
Sync dir: /home/user/SharePoint/Project Alpha - Reports
Drive ID: b!abc123...
Service: onedrive-sharepoint-ProjectAlpha_Reports (running)Removing a Sync
Interactive mode (shows a picker):
odopen-handler.sh --unsyncOr specify the name directly:
odopen-handler.sh --unsync "Project Alpha - Reports"The unsync command will:
- Stop and disable the systemd service
- Remove the configuration directory
- Optionally delete the local files (it will ask)
Useful systemd Commands
# Check sync status
systemctl --user status onedrive-sharepoint-<name>
# View live sync logs
journalctl --user -u onedrive-sharepoint-<name> -f
# Stop a sync temporarily
systemctl --user stop onedrive-sharepoint-<name>
# Restart a stopped sync
systemctl --user start onedrive-sharepoint-<name>How It Works Under the Hood
When you click “Sync” in SharePoint, the browser opens a URL like:
odopen://sync?siteId={...}&webId={...}&webTitle=MyProject&listTitle=Documents
&folderName=Reports&folderUrl=https://tenant.sharepoint.com/sites/MyProject/...The handler script:
- Parses the URL to extract the site name, library title, folder name, and relative path
- Derives a unique config using the site and folder names, creating a separate onedrive configuration directory per sync
- Queries the SharePoint API via
onedrive --get-sharepoint-drive-idto find the correctdrive_idfor the document library - Handles localized library names - SharePoint sends “Documents” in the URL but the API might return “Dokumente” (German). The script auto-selects if there’s only one library
- Creates a systemd user service that runs
onedrive --monitorwith the specific--confdirand optional--single-directoryflag - Uses an env file instead of sed for variable passing to the first-run script, avoiding issues with special characters like
&in site names
Gotchas and Tips
Don’t sync your entire personal OneDrive if you don’t need it. The default onedrive service is disabled. Only SharePoint syncs you explicitly set up via the “Sync” button will run.
Let the setup terminal finish. When the first-run terminal opens, let it complete all three steps before closing. The Drive ID gets written to the config during this process.
SharePoint uses localized names. The “Sync” button sends English names like “Documents”, but the API returns localized names like “Dokumente”. The script handles this automatically when there’s only one document library on the site.
Site names with special characters work. Names like “Jung Garten & Landschaft” are handled correctly through escaped environment variables.
The sync interval is 5 minutes by default. The onedrive client in monitor mode checks for changes every 300 seconds. You can adjust this with monitor_interval in the config file.
File indexers can cause issues. If you use Baloo or Tracker, consider excluding your ~/SharePoint/ directory from indexing to avoid accidental file modifications being synced upstream.
Written by Dominic Böttger
← Back to blog