A Docker container that syncs files one-way from a source directory into a Cryptomator vault, enabling encrypted storage while keeping the vault accessible with the official Cryptomator app.
Optionally, the encrypted vault can be synced to one or more upstream destinations with rclone, for example Google Drive, OneDrive, SFTP, or another rclone-supported provider.
π /sync
β rsync
π /vault-decrypted
β Cryptomator CLI
π /vault-encrypted
β rclone (optional)
β¬οΈ upstream destination(s)
- π‘ Use case
- β What this project does not do
- βοΈ Features
- π Requirements
- π Directory layout and volumes
- βοΈ Configuration
- π» Docker run
- π§© Docker Compose
- π Network mode
- π Sync modes
- β¬οΈ Rclone upstream sync
- π Healthcheck, state files, and restarts
- π·οΈ Image tags
- π Exit codes
- π Security notes
- β Backup verification
- π§ Development and testing
This project was built for a NAS backup scenario:
- Important files are stored on a Synology NAS or another host.
- These files should be backed up offsite.
- The offsite backup should be encrypted before it leaves the host.
- The encrypted vault should still be accessible with regular Cryptomator clients, for example from a laptop or phone.
A typical Synology setup can look like this:
/sync source dirs -> container -> local encrypted vault -> Synology Cloud Sync -> Google Drive/OneDrive
In that setup, rclone is not required inside this container because the host handles the upstream sync.
If the host does not provide a suitable cloud sync mechanism, the optional rclone upstream sync can sync /vault-encrypted to one or more remote destinations.
This is not a bidirectional sync tool.
Files that already exist inside the Cryptomator vault are not copied back to /sync. The sync direction is always:
/sync -> Cryptomator vault
PUIDandPGIDto ensure correct file permissions for mounted host directories, especially on NAS systems.- One-way sync from a plain source directory into a Cryptomator vault
- Docker-based one-shot or cron-based scheduled operation
- Non-overlapping scheduled sync cycles via internal locking
FUSEmount modersyncbased file transfer- Optional
RSYNC_DELETE=true - Optional rsync exclude file
- Optional dry-run mode
- Optional password file support
- Optional rclone upstream sync to one or more destinations
- Optional upstream verification via
rclone check - Internal decrypted vault mount point
- Clean shutdown and unmount handling
- Healthcheck and state files for monitoring
- Simple exit-code behavior
The container needs permission to create FUSE or WebDAV mounts inside the container.
For the current architecture, use:
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse:/dev/fuse
security_opt:
- apparmor:unconfinedapparmor:unconfined may not be needed on every host. If your setup works without it, you can omit it.
A typical host-side setup can look like this:
/docker/cryptomator-vault-sync/
βββ sync/
β βββ Documents/
β βββ Photos/
β βββ Important.txt
βββ vault-encrypted/
β βββ vault.cryptomator
β βββ masterkey.cryptomator
β βββ d/
βββ config/
β βββ vault-password
β βββ rclone.conf
β βββ rsync-exclude.txt
βββ state/
βββ current-status
βββ last-success
βββ last-error
These directories are mounted into the container as:
| Container path | Required | Recommended mode | Description |
|---|---|---|---|
/sync |
yes | read-only | Source files that should be copied into the decrypted vault view. |
/vault-encrypted |
yes | read-write | Existing initialized Cryptomator vault. |
/config |
optional | read-only | Optional config files such as vault-password, rclone.conf, and rsync-exclude.txt. |
/state |
optional | read-write | Status files used by the healthcheck and external monitoring. |
Source directory containing files that should be copied into the vault.
Recommended mount mode: read-only.
-v /path/to/sync:/sync:roYou can also mount multiple host directories below /sync. All files below /sync will be one-way synced into the vault while preserving the subdirectory structure.
volumes:
- /path/to/dir1:/sync/dir1:ro
- /path/to/dir2:/sync/dir2:ro
- /path/to/dir3:/sync/dir3:roEncrypted Cryptomator vault directory. This is the directory you also open with the official Cryptomator app.
The directory must already contain an initialized Cryptomator vault. Create the vault beforehand using the official Cryptomator app.
-v /path/to/vault:/vault-encryptedInternal temporary mount point used by the container.
Do not mount this directory from the host. Even if /vault-decrypted is bind-mounted, the host usually will not see the decrypted FUSE/WebDAV mount contents because the mount is created inside the container's mount namespace.
Optional read-only configuration directory.
It can contain:
/config/vault-password
/config/rclone.conf
/config/rsync-exclude.txt
Example config files:
rsync-exclude.txtvault-password.examplerclone.confcan be generated with an interactive rclone container
Optional writable state directory for status files.
The container writes three files:
| File | Description |
|---|---|
current-status |
Current container status. Used by the healthcheck in scheduled mode. |
last-success |
Timestamp of the last fully successful real sync cycle. Not updated during dry-run mode. |
last-error |
Timestamp and message of the last error. |
Example:
/state/current-status
2026-05-30 22:10:00 idle
/state/last-success
2026-05-30 22:10:00
/state/last-error
2026-05-30 22:05:00 Rclone failed for destination 'gdrive:Vault' with exit code 1
Possible current-status values:
| Status | Meaning |
|---|---|
starting |
Container has started and is validating configuration. |
running |
A sync cycle is currently running. |
idle |
Last sync cycle completed successfully and the container is waiting for the next cycle. |
upstream-error |
Local vault sync completed, but optional upstream sync failed. |
failed |
The container hit a fatal error and is exiting. |
stopped |
The container stopped cleanly. |
Important
The configured PUID/PGID must have read access to /sync
and write access to /vault-encrypted and /state.
use id command to find the correct ids
id <user>| Variable | Default | Description |
|---|---|---|
PUID |
1000 |
User ID used to run the sync process, including Cryptomator CLI and rsync. |
PGID |
1000 |
Group ID used to run the sync process. |
UMASK |
022 |
File creation mask used by the sync process. 022 is suitable for most single-user setups; 002 can be useful on shared-folder setups where group-write access is required, see also CRYPTOMATOR_VAULT_FIX_PERMISSIONS. |
BEFORE_SYNC_SCRIPT |
empty | Optional executable script path inside the container. Runs as PUID:PGID before the vault is mounted and synced. |
AFTER_SYNC_SCRIPT |
empty | Optional executable script path inside the container. Runs as PUID:PGID after sync, permission fix, and upstream sync. |
CRYPTOMATOR_VAULT_PASSWORD |
required if no password file is used | Password for the Cryptomator vault. If both password variables are set, this value takes precedence. |
CRYPTOMATOR_VAULT_PASSWORD_FILE |
unset | Full path to a file containing the Cryptomator vault password. Used only when CRYPTOMATOR_VAULT_PASSWORD is not set. |
CRYPTOMATOR_VAULT_FIX_PERMISSIONS |
false |
Fixes encrypted Cryptomator vault permissions after sync by adding user/group read-write access and setting the setgid bit on directories. Useful for shared-folder setups. |
CRYPTOMATOR_MOUNT_MODE |
fuse |
Mount/sync mode. fuse performs the local rsync sync. webdav starts the Cryptomator WebDAV endpoint but sync is currently not supported. See Cryptomator mount modes |
DRY_RUN |
false |
Runs rsync in dry-run mode and skips upstream sync. No files are written to the vault or upstream destinations. /state/last-success is not updated. |
SYNC_DIR |
/sync |
Source directory inside the container. |
VAULT_ENCRYPTED_DIR |
/vault-encrypted |
Encrypted vault directory inside the container. |
STATE_DIR |
/state |
Directory for state files. |
RSYNC_DELETE |
false |
If true, delete files in the vault that no longer exist in /sync. Only enable this if /sync is the authoritative source. Use DRY_RUN=true first to review what would be deleted. |
RSYNC_INPLACE |
false |
Enables rsync --inplace when set to true. Disabled by default. |
RSYNC_EXCLUDE_FILE |
empty | Optional path to an rsync exclude file. See Rsync exclude file. |
RSYNC_ARGS |
-rtvi --no-owner --no-group --no-perms |
Base rsync arguments. |
RSYNC_EXTRA_ARGS |
empty | Additional rsync arguments. |
MOUNT_TIMEOUT_SECONDS |
60 |
Timeout for mount operations. |
SYNC_CRON |
empty | Cron schedule for scheduled mode. Leave empty for one-shot mode. Uses standard 5-field cron syntax, for example 0 * * * *. |
UPSTREAM_ENABLED |
false |
Enable optional rclone upstream sync after the encrypted vault has been updated. |
UPSTREAM_CHECK |
false |
If true, runs rclone check after each successful upstream sync/copy destination. This verifies that source and destination match, but can increase runtime and provider API usage. |
UPSTREAM_FAIL_ACTION |
continue |
Behavior when rclone or upstream check fails. continue marks the status as upstream-error and retries on the next scheduled cycle. exit marks the sync cycle as failed. One-shot mode always exits on upstream errors. |
UPSTREAM_MODE |
sync |
rclone operation mode. sync mirrors the local encrypted vault to the destination, including deletions. This is the recommended mode when the upstream destination should be an exact copy of the local vault. copy uploads new and changed files without deleting remote files, but may leave old encrypted vault files at the destination. |
UPSTREAM_DESTINATIONS |
empty | One or more rclone destination paths separated by ` |
UPSTREAM_CONFIG |
/config/rclone.conf |
Path to the rclone configuration file. |
UPSTREAM_EXTRA_ARGS |
empty | Additional arguments passed to rclone. |
UPSTREAM_START_DELAY_SECONDS |
0 |
Optional delay after unmounting the vault before running rclone. |
PUID, PGID, and UMASK define the user, group, and file creation mask used by the sync process. This is important when the encrypted Cryptomator vault is stored on a mounted host directory, especially on NAS systems or shared folders.
For most single-user Linux setups, the defaults are usually sufficient:
PUID=1000
PGID=1000
UMASK=022
CRYPTOMATOR_VAULT_FIX_PERMISSIONS=falseOn NAS or shared-folder setups, the encrypted vault may also be opened from another device over SMB or another network share. In this case, the container may need to run with the same user ID as the share user, but with a different effective group ID.
UMASK=002 creates group-writable files and directories. This can help avoid permission issues when files created by the container are later modified or deleted from another device.
CRYPTOMATOR_VAULT_FIX_PERMISSIONS=true fixes permissions on the encrypted Cryptomator vault after each sync. It adds user/group read-write access and sets the setgid bit on directories. This can be required when the vault is accessed through a network share and files need to be modified or deleted from another device.
Make sure the configured PUID and PGID have read access to /sync and read/write access to /vault-encrypted, /state, and any mounted config files.
A typical NAS/shared-folder setup can look like this:
PUID=1000
PGID=1000
UMASK=002
CRYPTOMATOR_VAULT_FIX_PERMISSIONS=trueSynology Cloud Sync may not reliably detect changes written by Docker containers through direct bind mounts. In that case, new encrypted vault files may only be uploaded after a Cloud Sync rescan or service restart.
On Synology systems, mounting the encrypted vault through an NFS-backed Docker volume instead of a direct bind mount can help Cloud Sync detect container-written changes automatically.
To enable this on Synology:
- Go to
Control Panel->File Services->NFS. - Enable NFS. NFS v4.1 is recommended, but NFS v3 can also work if needed.
- Go to
Control Panel->Shared Folder. - Select the shared folder that contains your encrypted vault and click
Edit. - Open
NFS Permissionsand create a rule for127.0.0.1. - Tick allow access to subfolders.
- Save the settings.
Example Docker Compose configuration:
services:
cryptomator-vault-sync:
volumes:
- cloud-nfs-vault:/vault-encrypted
- ...
volumes:
cloud-nfs-vault:
driver: local
driver_opts:
type: nfs
o: addr=127.0.0.1,nfsvers=4,rw
device: ":/volume1/Cloud/MyVault"Hook scripts run inside the container as the configured PUID/PGID. They are intended for lightweight custom logic and are not suitable for host-level service management.
BEFORE_SYNC_SCRIPT=/config/before-sync.sh
AFTER_SYNC_SCRIPT=/config/after-sync.shNote
WebDAV currently starts the Cryptomator WebDAV endpoint but does not perform the local sync yet. Support is planned, but currently not possible due to an issue.
| Option | Meaning |
|---|---|
fuse |
Uses Cryptomator CLI's Linux FUSE mount provider. |
webdav |
NOT SUPPORTED YET Uses Cryptomator CLI's WebDAV fallback mounter and mounts it internally. |
To check FUSE availability on the host:
ls -l /dev/fuseA typical successful result looks like:
crw-rw-rw- 1 root users 10, 229 ... /dev/fuse
Only enable RSYNC_DELETE=true if /sync is the authoritative source.
When enabled, files that no longer exist in /sync will also be deleted from the vault during sync.
Before enabling this option for the first time, run with DRY_RUN=true and review the rsync output.
RSYNC_INPLACE controls whether rsync uses --inplace. Inplace writes updated files directly instead of using rsync's default temporary-file-and-rename behavior.
RSYNC_INPLACE=falseRSYNC_ARGS=-rtvi --no-owner --no-group --no-perms| Option | Meaning |
|---|---|
-r |
Copy directories recursively. |
-t |
Preserve modification times. |
-v |
Enable verbose output. |
-i |
Show itemized changes in the logs. |
--no-owner |
Do not preserve file owner. |
--no-group |
Do not preserve file group. |
--no-perms |
Do not preserve file permissions. |
RSYNC_EXTRA_ARGS can be used to pass additional arguments to rsync. These arguments are appended to the default RSYNC_ARGS. For all available options, see the rsync(1) man page.
Be aware that RSYNC_ARGS and RSYNC_EXTRA_ARGS are included in the logged rsync arguments, so sensitive values passed there may appear in the container logs.
# Use checksums instead of size and modification time to detect changed files
RSYNC_EXTRA_ARGS=--checksum
# Skip files that already exist in the vault
RSYNC_EXTRA_ARGS=--ignore-existing
# Skip files larger than 500M
RSYNC_EXTRA_ARGS=--max-size=500M
# Limit bandwidth to approximately 5000 KiB/s
RSYNC_EXTRA_ARGS=--bwlimit=5000You can exclude files or directories from the local sync with an rsync exclude file. The file is passed to rsync via --exclude-from.
Patterns are interpreted relative to the /sync source directory. See example rsync-exclude.txt
RSYNC_EXCLUDE_FILE=/config/rsync-exclude.txtUPSTREAM_MODE controls how rclone writes /vault-encrypted to the configured upstream destination.
| Option | Meaning |
|---|---|
sync |
Mirrors the local encrypted vault to the destination, including deletions. This is recommended when the upstream destination should be an exact copy of the local Cryptomator vault. |
copy |
Uploads new and changed files without deleting remote files. This can be useful for conservative uploads, but it may leave old encrypted vault files at the destination and should not be treated as an exact mirror. |
When enabled, the container runs rclone check for each upstream destination after rclone sync or rclone copy completed successfully.
This can help detect incomplete or inconsistent upstream transfers, but it may increase runtime and provider API usage. For cloud providers with strict rate limits, consider reducing rclone concurrency via UPSTREAM_EXTRA_ARGS.
UPSTREAM_CHECK=trueUPSTREAM_EXTRA_ARGS can be used to pass additional arguments to rclone. These arguments are appended to the rclone command. See the official rclone global flags documentation.
The effective rclone command arguments are logged before execution to make debugging easier. Be aware that UPSTREAM_EXTRA_ARGS is included in the logged rclone arguments, so sensitive values passed there may appear in the container logs.
# Limit rclone bandwidth to 8M
UPSTREAM_EXTRA_ARGS=--bwlimit 8M
# Reduce parallel transfers and checks
UPSTREAM_EXTRA_ARGS=--transfers 2 --checkers 4
# Set the Google Drive upload chunk size
UPSTREAM_EXTRA_ARGS=--drive-chunk-size 64M
# Enable verbose rclone logging for debugging
UPSTREAM_EXTRA_ARGS=-vvMinimal dry-run example without rclone:
docker run --rm -it \
--network none \
-v /path/to/sync:/sync:ro \
-v /path/to/vault:/vault-encrypted \
-v /path/to/state:/state \
--cap-add SYS_ADMIN \
--device /dev/fuse:/dev/fuse \
--security-opt apparmor:unconfined \
-e CRYPTOMATOR_VAULT_PASSWORD='MyVaultPassword' \
-e DRY_RUN='true' \
ghcr.io/chrschu90/cryptomator-vault-sync:1Choose the example that matches your upstream sync strategy:
| File | Use case |
|---|---|
docker-compose.no-upstream.yml |
Local encrypted vault only. Use this when the host handles upstream sync externally, for example with Synology Cloud Sync, Google Drive Desktop, or OneDrive. |
docker-compose.rclone-upstream.yml |
Container-managed upstream sync via rclone. |
docker-compose.full.yml |
Full reference example with all relevant options. |
You can also use an environment file:
env_file:
- .envMinimal dry-run example without rclone:
services:
cryptomator-vault-sync:
image: ghcr.io/chrschu90/cryptomator-vault-sync:1
container_name: cryptomator-vault-sync
network_mode: none
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse:/dev/fuse
security_opt:
- apparmor:unconfined
environment:
CRYPTOMATOR_VAULT_PASSWORD: MyVaultPassword
DRY_RUN: true
volumes:
- /path/to/sync:/sync:ro
- /path/to/vault:/vault-encrypted
- /path/to/config:/config:ro
- /path/to/state:/stateIf UPSTREAM_ENABLED=false, the container does not need outbound network access during normal operation.
In this mode you can disable networking:
network_mode: noneor:
--network noneIf UPSTREAM_ENABLED=true, the container needs network access so rclone can reach the configured destination, for example another machine in the local network, Google Drive, or OneDrive.
Use Docker's default bridge network or omit network_mode.
Leave SYNC_CRON empty:
SYNC_CRON=The container runs one sync cycle and exits.
- Unlock the vault.
- Sync files from
/syncinto the decrypted vault view. - Unmount the vault.
- Optionally run rclone against
/vault-encrypted. - Exit.
Use this mode with an external scheduler such as cron or Synology Task Scheduler.
Scheduled mode is handled by supercronic inside the container. Each scheduled run starts /sync.sh. Sync cycles are protected against overlap by a lock file, so a scheduled run is skipped if the previous sync cycle is still running.
Set a cron expression:
SYNC_CRON=0 * * * *In Docker Compose, quote cron expressions:
SYNC_CRON: "0 * * * *"The container starts a sync cycle according to the cron schedule. Sync cycles are protected against overlap. If a previous cycle is still running when the next scheduled run starts, that run is skipped.
Each cycle will:
- Unlock the vault.
- Sync files from
/syncinto the decrypted vault view. - Unmount the vault.
- Optionally run rclone against
/vault-encrypted. - Wait until the next cycle.
The decrypted vault is not kept mounted between cycles. This is intentional: rclone or host-side sync tools should see a stable, closed encrypted vault state instead of files that Cryptomator is still updating.
Set:
DRY_RUN=trueDry-run mode:
- Unlocks and mounts the vault normally.
- Runs rsync with
--dry-run. - Does not write files to the vault.
- Skips rclone/upstream sync.
- Does not update
/state/last-successbecause no real sync was performed.
This is useful for checking what rsync would copy or delete before enabling a real sync, especially when using RSYNC_DELETE=true.
rclone is optional. Enable it only when the container itself should upload or copy the encrypted vault to one or more upstream destinations.
UPSTREAM_ENABLED=true
UPSTREAM_DESTINATIONS=gdrive:CryptomatorVault
UPSTREAM_CONFIG=/config/rclone.confCreate an rclone config interactively:
docker run --rm -it \
-v /path/to/config:/config \
rclone/rclone config --config /config/rclone.confThe remote name is the section name in rclone.conf:
[gdrive] # <-- remote name
type = drive
token = ...
[onedrive]
type = onedrive
token = ...
Multiple destinations are separated by |:
UPSTREAM_DESTINATIONS=gdrive:CryptomatorVault|onedrive:CryptomatorVaultSpaces around | are ignored. Avoid using | in remote folder names.
If your vault should be placed inside a subdirectory of the remote, for example:
Root/
βββ Vaults/
βββ Backup Vault/
set:
UPSTREAM_DESTINATIONS=gdrive:Vaults/Backup VaultUPSTREAM_MODE controls whether rclone uses copy or sync. See Configuration.
In one-shot mode, the container exits after one sync cycle. The container exit code is the primary status signal.
In scheduled mode, Docker runs /healthcheck.sh. The healthcheck reads /state/current-status:
starting,running,idle, andstoppedare healthy states.- unknown states,
failed, andupstream-errorare unhealthy states.
Docker's HEALTHCHECK --retries=3 means the container is only marked unhealthy after repeated failing checks. Once a later cycle succeeds and writes idle, the container becomes healthy again.
Recommended restart policies:
| Mode | Restart policy | Reason |
|---|---|---|
| One-shot with external scheduler | restart: "no" |
The scheduler should see the container exit code. |
| Scheduled mode | restart: unless-stopped |
Docker can restart the container after fatal runtime errors. |
If UPSTREAM_FAIL_ACTION=continue is set in scheduled mode, upstream errors do not stop the container. Instead, the container writes upstream-error to /state/current-status, writes the error to /state/last-error, and retries during the next sync cycle.
This image follows semantic versioning. Use specific version tags for reproducibility. Preview tags are not recommended for production.
latestβ Most recent stable release1β Latest stable release in major version11.2β Latest stable release in minor version1.21.2.3β Specific stable patch version (fully pinned)previewβ Latest preview build1-previewβ Latest preview for major version11.2-previewβ Latest preview for minor version1.21.2.3-previewβ Latest preview for patch version1.2.31.2.3-beta.1β Specific preview build (fully pinned)
| Exit code | Meaning |
|---|---|
0 |
Success or clean stop via CTRL+C / docker stop. |
1 |
Runtime error, mount error, rsync error, or upstream error. |
2 |
Invalid configuration. |
This container needs elevated mount permissions to unlock and mount a Cryptomator vault inside the container. Treat it as a privileged workload and only mount the host paths it really needs.
Recommended checklist:
| Area | Recommendation |
|---|---|
/sync |
Mount read-only whenever possible. |
/config |
Mount read-only and protect files such as vault-password and rclone.conf. |
/state |
Keep writable, but do not store secrets there. |
/vault-decrypted |
Do not mount from the host. It is an internal temporary mount point. |
| Host mounts | Avoid broad mounts such as /, /volume1, or a full home directory. |
| Network | Use network_mode: none when UPSTREAM_ENABLED=false. |
| rclone | Protect rclone.conf, because it may contain cloud access tokens. |
| Passwords | Prefer CRYPTOMATOR_VAULT_PASSWORD_FILE over putting the vault password directly into Compose files. |
A backup is only useful if it can be opened and the expected files are readable.
This project writes data into a standard Cryptomator vault. Verification should therefore be done with the official Cryptomator app or another trusted Cryptomator client.
Recommended verification steps:
- Let the container finish a successful real sync cycle.
- Check
/state/last-successto confirm when the last successful sync happened. - Make sure the encrypted vault has been synced to the upstream destination.
- On another device, download or sync the encrypted vault from the upstream destination.
- Open the vault with the official Cryptomator app.
- Verify that important files are visible and readable.
- Repeat this regularly.
Do not only check that encrypted files exist in the remote destination. The important test is whether the vault can be unlocked and the expected decrypted files can be read.
If the upstream sync is handled by the host, for example Synology Cloud Sync, also verify that the host-side sync has completed before opening the vault on another device.
DRY_RUN=true does not update /state/last-success and does not create a real backup. Use a real sync cycle for backup verification.
The container entrypoint and sync logic are split into multiple shell scripts:
| File | Purpose |
|---|---|
scripts/common.sh |
Shared helper functions for logging, timestamps, state files, exit handling, and small utility functions. |
scripts/config.sh |
Central place for defaults, configuration validation, runtime path validation, and vault password loading. |
scripts/run.sh |
Container entrypoint. Selects one-shot or scheduled mode, validates startup configuration, and starts supercronic when SYNC_CRON is set. |
scripts/sync.sh |
Executes one complete sync cycle: validates runtime paths, loads the vault password, mounts the vault, runs rsync, unmounts the vault, and optionally runs rclone/upstream checks. |
scripts/healthcheck.sh |
Docker healthcheck script. In scheduled mode, reads /state/current-status and maps known states to healthy or unhealthy. |
debug.sh |
Local helper script for manual image builds, debug runs, and interactive testing during development. |
For manual debugging, sync.sh can also be executed directly inside a running container to trigger one sync cycle.
docker exec -it cryptomator-vault-sync /sync.shThis does not start the scheduler. It only runs one sync cycle. If another sync cycle is already running, the internal lock prevents overlapping runs.
Run the local test suite with:
./tests/test.shThe test script:
- Checks shell syntax for all project scripts:
- common.sh
- config.sh
- run.sh
- sync.sh
- healthcheck.sh
- Builds the Docker image without cache.
- Validates configuration errors.
- Runs one-shot sync integration tests.
- Tests scheduled-mode configuration and healthcheck behavior.
- Tests rclone/upstream behavior.
- Tests optional upstream verification with UPSTREAM_CHECK=true.
- Tests state files and status handling.
The tests require Docker Buildx and a host environment that supports the required container mount permissions.