onedrive-sync-station-beta

onedrive-sync-station-beta

Docker app from benjaminmue's Repository

Overview

BETA CHANNEL. Pre-release builds from the beta branch, published for testing before they are released. They may break. Do not point it at data you have no other copy of. For daily use install the stable entry onedrive-sync-station instead. This entry can run next to the stable one: it defaults to port 8495 and to separate -beta paths. Never point it at the same Config or Data path as the stable container. Two clients syncing into one folder cause conflicts and can lose data, and a shared Config path mixes their sign-ins. Syncs several Microsoft OneDrive accounts at once: OneDrive Personal, OneDrive Business and SharePoint document libraries, each with its own folder, its own settings and its own sign-in. Everything is managed from a web UI, including the Microsoft sign-in itself, so no terminal or docker exec is needed. Built on the OneDrive Client for Linux by abraunegg (GPL-3.0), compiled into the image from a pinned release tag. Each account runs its own client process inside this single container, so there is no need for one container per account and no access to the Docker socket. Sign-in needs no client secret and normally no app registration of your own. Business and SharePoint accounts can use a device code: the UI shows a short code, you enter it on one Microsoft page, and nothing has to be copied back. Personal accounts cannot, because Microsoft blocks that flow for them, so they use a link and paste the address of the resulting blank page back into the UI. If your tenant requires administrator approval, the UI produces the admin consent URL for you. Nothing downloads behind your back: a new account does not start syncing on its own. It reads its folder list first, in a run that downloads nothing, so the choice comes before the traffic. Folders are picked from a list, backed by an editor for the client's sync_list rules and a dry-run preview. Config and data are kept apart: the Config path holds settings and sign-ins, the Data path holds nothing but synced files, one subfolder per account. LAN-only by design: the web UI can sign in to Microsoft accounts and reaches every synced file, so do NOT expose it to the internet without a reverse proxy that adds its own authentication.

OneDrive Sync Station

OneDrive Sync Station

Sync several OneDrive Personal, OneDrive Business and SharePoint accounts from one container, managed through a web UI. Built on the OneDrive Client for Linux. Made for Unraid.


🤖 Built with AI, disclosed openly. This project is developed with heavy assistance from AI (Anthropic's Claude, via Claude Code): code, tests, and documentation. This is stated up front, not hidden. It's used for a personal homelab; review the code yourself before trusting it with your data, and treat it accordingly. Issues and PRs are welcome.

Status: early. All three account types have been synced end to end on real hardware: a personal account, a business account and a SharePoint document library, each through sign-in, folder selection, download, upload and deletion in both directions. It is still young software with one pair of eyes on it. Do not point it at data you have no other copy of.

What it does

  • Several accounts, one container. Each account runs its own sync client with its own configuration and its own folder. No Docker socket, no container per account.
  • Personal, Business and SharePoint. All three sign in the same way. A SharePoint document library is added as an account of its own; the UI can look up the drive ID of a site with the credentials of a business account that is already signed in.
  • Sign-in from the browser. No terminal, no docker exec. The UI shows the Microsoft sign-in link and takes the redirect URL back.
  • Selective sync. Per account, a folder list to tick, backed by an editor for the client's sync_list rules and a dry-run preview. Saving triggers the resync the client requires after every change.
  • Nothing downloads behind your back. A new account does not start syncing on its own. It reads its folder list from Microsoft Graph first, which is quick and downloads nothing, so the choice comes before the traffic. Folders that exist only on this server are marked as such, because those are the ones with no copy anywhere else.
  • Config and data kept apart. /config holds settings and sign-ins, /data holds nothing but synced files, one subfolder per account.
  • Password protected. The UI is gated behind a password of its own, recoverable through an environment variable.

Quick start

docker run -d \
  --name onedrive-sync-station \
  -p 8080:8080 \
  -v /mnt/user/appdata/onedrive-sync-station:/config \
  -v /mnt/user/OneDrive:/data \
  -e PUID=99 -e PGID=100 -e TZ=Europe/Zurich \
  ghcr.io/benjaminmue/onedrive-sync-station:latest

Then open http://<host>:8080, set a password, and add your first account.

Channels

There are two channels, each with its own image tag and its own Community Applications entry:

Channel Image tag Built from CA entry
Release :latest (also :X.Y.Z, :X.Y) version tags vX.Y.Z on main onedrive-sync-station
Beta :beta every push to the beta branch onedrive-sync-station-beta

Every change lands on beta first, is tested there, and is released afterwards. Use :latest unless you want to test what comes next.

Beta and release must not share the same /config or /data path. If you run both, give each its own appdata folder and its own data share: two stations working on the same sign-ins and the same files will get in each other's way.

Install on Unraid

The container is in Community Applications. Search for OneDrive Sync Station under Apps and pick onedrive-sync-station for the stable release (onedrive-sync-station-beta is the beta channel).

The CA template is maintained in the repository benjaminmue/unraid. To add the container without CA, use the Docker tab, Add Container, and paste that template URL:

https://raw.githubusercontent.com/benjaminmue/unraid/main/templates/onedrive-sync-station.xml

Or with compose:

git clone https://github.com/benjaminmue/OneDrive-Sync-Station.git
cd OneDrive-Sync-Station
docker compose up -d --build

Adding an account

  1. Add account, give it a name and pick the type.
  2. Sign in. The UI shows a Microsoft link. Open it, sign in, and you land on a blank page.
  3. Copy the full URL of that blank page from the address bar and paste it back into the UI. That URL carries the authorisation code.
  4. Decide what to sync. Syncing does not start by itself: the account offers to look at its folders first (read from Microsoft, nothing is downloaded), to go straight to the selection, or to take everything. Pressing Start begins the sync.

Microsoft shows a warning on that blank page, claiming the URL contains your password and should not be shared. It carries a one-time code, not a password, and pasting it into the station is its intended use. Copy the address quickly: the page redirects itself after a few seconds, and the code is then only recoverable from the browser history.

Which sign-in method

Business and SharePoint can use the device code: the UI shows a short code, you enter it on one Microsoft page, and the client signs itself in. Nothing is copied back.

Personal accounts cannot. Microsoft blocks the device code grant for personal accounts (outlook.com, hotmail.com, and the like) unless it has explicitly approved the application, and refuses the code as expired even seconds after issuing it. That is documented upstream. Personal accounts therefore use the copy-and-paste method above, and the UI offers only that.

Nothing else is needed for Personal, Business or SharePoint. In particular:

  • No client secret, and normally no app registration of your own. The client is registered with Microsoft as a public client application using delegated permissions (Files.ReadWrite, Files.ReadWrite.All, Sites.ReadWrite.All, offline_access).
  • If your tenant requires an administrator to approve the application first, the account's admin consent URL button produces the link for them.
  • If your tenant insists on its own app registration, set the application ID (and optionally the tenant ID) in the account's options. That registration must be a public client with both of these redirect URIs, or sign-in fails with AADSTS50011:
    • http://127.0.0.1:53100/
    • https://login.microsoftonline.com/common/oauth2/nativeclient

SharePoint libraries

Sign in with a business account first, then use SharePoint lookup. It takes a site name or the address of the library as it appears in the browser, and returns the drive IDs of that site's document libraries. Create an account of type SharePoint library with the ID you want.

Selecting folders

Folders opens the sync_list editor of an account:

# exclusions first, they win over inclusions
!/Documents/temp*
!node_modules/*

# then what should be synced
/Documents/
/Pictures/Camera Roll/*

An empty list syncs everything. Rules without a leading slash match anywhere in the tree and are the expensive kind, because the client has to walk every folder online and locally to find them.

Above the editor is a list of the account's folders to tick, so the paths do not have to be typed by hand. It is read from Microsoft Graph with the account's own sign-in, and a running account keeps syncing meanwhile. The station redeems the client's refresh token for a short-lived access token in memory and never writes the token file. If Graph cannot be reached, it falls back to a dry run of the sync client, which lists the same folders but takes minutes on a large account. Reload list reads it again after folders were created in OneDrive.

A folder is marked only here when it exists on this server but not in the list read from Graph. After a dry-run fallback the marker is not shown at all, because a dry run leaves out the folders that are already synced, and it is never shown inside a shared folder, whose contents are not part of the list.

Saving restarts the account with --resync, which the client requires after every change to the selection. Note what that means for folders you remove: their local copies under the data path are deleted on the next run. They remain in OneDrive. Use Dry run first to see what would happen.

Configuration

Variable Default Purpose
WEBUI_PORT 8080 Port of the web UI inside the container
CONFIG_DIR /config Settings, per-account client config, refresh tokens
DATA_DIR /data Synced files, one subfolder per account
PUID / PGID 99 / 100 Ownership of everything the container writes
UMASK 0002 Keeps synced files group-writable, matching Unraid shares
TZ Europe/Zurich Timezone for the timestamps in the UI
ADMIN_PASSWORD unset Overwrites the web UI password on start, see below
FIX_PERMISSIONS true Repair ownership drift on start

Files over an SMB share

With the defaults PUID=99, PGID=100 and UMASK=0002 synced files are created as 0664 and folders as 0775, so an Unraid share can open them. Versions up to 0.6.0 left downloads at 0600 and 0700 instead. For files downloaded back then, open the account's Tools tab and use Repair file permissions once.

Lost the web UI password

Set ADMIN_PASSWORD in the container template, restart, sign in, then remove the variable again. The old password is replaced and all sessions are dropped.

Volume layout

/config
  settings.json              web UI password hash, cookie secret
  instances.json             the account registry
  instances/<account>/       client config, sync_list, refresh token, item database
  auth/                      scratch files for a sign-in in progress
/data
  <account>/                 synced files, nothing else

Security

  • The UI can sign in to Microsoft accounts and reads every synced file. It is meant for a LAN. Put a reverse proxy with its own authentication in front of it before exposing it to the internet.
  • Sync clients are started with an argument array, never through a shell. Every value that reaches a process argument, a directory name or the client config file is validated in one place (src/validate.js), which also rejects the quotes and newlines that would let a value inject extra client settings.
  • Refresh tokens live in /config at 0600 and never leave the container.
  • The station never sees your Microsoft password: you sign in on Microsoft's pages and only the resulting authorisation code passes through the UI.

Releasing

Every published image gets a version. The header of the web UI shows it next to the commit the image was built from, which is the only reliable way to tell whether an update has taken effect.

npm run release:patch   # fixes
npm run release:minor   # new capabilities

Then:

  1. Commit and merge into beta. That publishes :beta.
  2. Test the beta image on a real server.
  3. Open a pull request from beta to main and merge it.
  4. Tag the merge commit on main with vX.Y.Z. That publishes :latest, :X.Y.Z and :X.Y.

The publish workflow runs the tests first and refuses to build if they fail.

Development

npm install
npm test                       # unit and API tests, no container needed
ONEDRIVE_BIN=./test/fixtures/fake-onedrive.mjs \
CONFIG_DIR=./tmp/config DATA_DIR=./tmp/data npm start

The tests drive the real HTTP API against a stub client (test/fixtures/fake-onedrive.mjs) that implements the file based sign-in handshake and monitor mode, so the whole flow can be exercised without a Microsoft account or a container.

Module Responsibility
src/app.js HTTP routes
src/server.js Process entry: startup, shutdown, password recovery
src/instances.js Account registry, directory layout, client config rendering
src/supervisor.js Child process lifecycle, restart backoff, log buffers
src/authflow.js File based Microsoft sign-in handshake
src/onedrive.js Client command wrapper and output parsing
src/synclist.js Folder selection file
src/graph.js Folder tree read from Microsoft Graph, token redeemed in memory
src/discovery.js Folder listing runs: Graph first, a client dry run as fallback
src/foldertree.js Folder list, merged from every source that knows one
src/validate.js All input validation

Known gaps

Honest list of what is missing or rough, rather than finding out the hard way:

  • National clouds are not supported. The station has no setting for them; the folder list is read from the global Microsoft endpoints.
  • Folders inside shared folders are not listed. A folder shared into the account appears in the list and can be selected, its subfolders live on the owner's drive and have to be added as rules by hand.
  • No log rotation. The client's output is held in memory per account and capped by line count, but nothing is written to disk in a rotated form yet.
  • One pair of eyes. No independent review has happened yet.

Credits

All syncing is done by the OneDrive Client for Linux by @abraunegg, licensed GPL-3.0 and built into the image from source at a pinned tag. See NOTICE.md.

This project's own code is MIT licensed. It is not affiliated with Microsoft.

Related apps

Details

Repository
ghcr.io/benjaminmue/onedrive-sync-station:beta
Last Updated2026-10-05
First Seen2026-08-27

Runtime arguments

Web UI
http://[IP]:[PORT:8080]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Host port for the web UI (the container listens on 8080). The stable entry uses 8485, so this beta entry defaults to 8495. LAN-only, do not expose to the internet.

Target
8080
Default
8495
Value
8495
ConfigPathrw

Persistent state: web UI password, the account registry, per-account client config and the Microsoft refresh tokens. Back this up: losing it means signing every account in again. Must NOT be the appdata folder of the stable container.

Target
/config
Default
/mnt/user/appdata/onedrive-sync-station-beta
Value
/mnt/user/appdata/onedrive-sync-station-beta
DataPathrw

Where synced files land. One subfolder per account is created below this path automatically. Use a dedicated share; nothing but synced data belongs here. Never share it with the stable onedrive-sync-station container: two clients syncing into one folder cause conflicts.

Target
/data
Default
/mnt/user/OneDrive-beta
Value
/mnt/user/OneDrive-beta
TimezoneVariable

Container timezone. Drives the timestamps shown in the web UI.

Target
TZ
Default
Europe/Zurich
Value
Europe/Zurich
PUIDVariable

User ID the container runs as. Default 99 (nobody) so synced files stay usable by other containers.

Default
99
Value
99
PGIDVariable

Group ID the container runs as. Default 100 (users), the Unraid share convention.

Default
100
Value
100
UMASKVariable

File mode mask for everything the container writes. 0002 gives 0664 files and 0775 folders, so the group keeps write access.

Default
0002
Value
0002
Admin password resetVariable

Recovery only: set this to overwrite the web UI password on the next start, sign in, then clear this field again. Leave empty during normal operation.

Target
ADMIN_PASSWORD
Repair permissionsVariable

Repairs ownership of the config and data paths on start. Set to false only if you manage permissions on the host yourself.

Target
FIX_PERMISSIONS
Default
true
Value
true