All apps · 0 apps
onedrive-sync-station-beta
Docker app from benjaminmue's Repository
Overview
Readme
View on GitHub
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_listrules 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.
/configholds settings and sign-ins,/dataholds 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
- Add account, give it a name and pick the type.
- Sign in. The UI shows a Microsoft link. Open it, sign in, and you land on a blank page.
- 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.
- 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
/configat0600and 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:
- Commit and merge into
beta. That publishes:beta. - Test the beta image on a real server.
- Open a pull request from
betatomainand merge it. - Tag the merge commit on
mainwithvX.Y.Z. That publishes:latest,:X.Y.Zand: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.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/benjaminmue/onedrive-sync-station:betaRuntime arguments
- Web UI
http://[IP]:[PORT:8080]/- Network
bridge- Shell
sh- Privileged
- false
Template configuration
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
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
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
Container timezone. Drives the timestamps shown in the web UI.
- Target
- TZ
- Default
- Europe/Zurich
- Value
- Europe/Zurich
User ID the container runs as. Default 99 (nobody) so synced files stay usable by other containers.
- Default
- 99
- Value
- 99
Group ID the container runs as. Default 100 (users), the Unraid share convention.
- Default
- 100
- Value
- 100
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
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
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