How to update ?
Minor version
Docker
Pull the new image and restart the container.
docker compose pull
docker compose up -d
That’s it.
Debian (manual install)
Backup your config files
Before starting, make sure you have a safe copy of :
.env.localconfig/users.jsonconfig/repo.json
Stop the service
systemctl stop borgwarehouse
Fetch the new version
su borgwarehouse
cd borgwarehouse
git pull
Install dependencies
pnpm install --frozen-lockfile
Build the application
pnpm run build
Restart
BorgWarehouse is up-to-date 🥳 !
systemctl start borgwarehouse
Docker migration - v3.1.3 (PUID/PGID)
Migrating to v3.1.3 - Docker
Previous versions required the Docker image to be built with a specific UID and GID baked in at compile time. The official image on Docker Hub was built with 1001:1001, which meant anyone running a different user on their host had to rebuild the image themselves. This was a significant friction point, especially on NAS and other self-hosted platforms where the user and group IDs are fixed by the system.
Starting with this version, the container handles user mapping at runtime (just like other projects like linuxserver.io). You set PUID and PGID in your .env file, and the container remaps its internal user to match on startup. No rebuild needed.
Two other things changed as a result:
sshdnow runs as root inside the container. This is the standard behavior for OpenSSH — it requires root to handle host keys and privilege separation. Running it as a non-root user was a workaround. This is the same pattern used by projects like Linuxserver.io and atmoz/sftp (1B+ Docker pulls). The security posture is unchanged:PermitRootLogin no, public key only,AllowUsers borgwarehouse.- The
user:directive has been removed fromdocker-compose.yml. User mapping is now handled by the entrypoint script, not by Docker.
TL;DR : BorgWarehouse itself never runs as root.
What you need to do
1. Update your .env file
Replace UID and GID with PUID and PGID:
- UID=1001
- GID=1001
+ PUID=1001
+ PGID=1001
Use the same values you had before. If you were already using 1001:1001, just rename the variables.
To find the UID and GID of your host user:
id
2. Update your docker-compose.yml
Remove the user: line and the build args if you had them:
borgwarehouse:
container_name: borgwarehouse
- build:
- context: .
- dockerfile: Dockerfile
- args:
- - UID=${UID}
- - GID=${GID}
image: borgwarehouse/borgwarehouse
- user: '${UID}:${GID}'
The final file should look like this:
services:
borgwarehouse:
container_name: borgwarehouse
image: borgwarehouse/borgwarehouse
ports:
- '${WEB_SERVER_PORT}:3000'
- '${SSH_SERVER_PORT}:22'
env_file:
- .env
volumes:
- ${CONFIG_PATH}:/home/borgwarehouse/app/config
- ${SSH_PATH}:/home/borgwarehouse/.ssh
- ${SSH_HOST}:/etc/ssh
- ${BORG_REPOSITORY_PATH}:/home/borgwarehouse/repos
3. Check your volume permissions
Your host directories must be owned by the user matching PUID:PGID. This was already the case before, nothing changes here. If you were running with 1001:1001 and your folders were owned by that user, they still need to be.
If you are unsure:
ls -ln ./config ./ssh ./ssh_host ./repos
The third and fourth columns are the numeric UID and GID. They must match PUID and PGID.
To fix ownership if needed:
chown -R YOUR_PUID:YOUR_PGID ./config ./ssh ./ssh_host ./repos
4. Reset your sshd_config
The sshd_config file in your ssh_host volume was generated by a previous version and contains an outdated PidFile path. Delete it so the new one is copied automatically on startup:
rm ./ssh_host/sshd_config
If you had customized your sshd_config, note down your changes before deleting it and reapply them after the container has started.
5. Pull the new image and restart
docker compose pull
docker compose up -d
If something goes wrong
The container now prints a clear error and exits if a volume is missing or has wrong permissions. Check the logs:
docker logs borgwarehouse
Examples of what you might see:
[ERROR] Volume 'repos' is not mounted. Expected path: /home/borgwarehouse/repos
Check the volumes section in your docker-compose.yml.
[ERROR] Volume 'config' (/home/borgwarehouse/app/config) is not writable by UID=1000 GID=1000.
Fix on the host: chown -R 1000:1000 <your-host-path-for-config>
For people who were building their own image
You no longer need to build the image yourself just to set a custom UID/GID. Pull the official image and set PUID/PGID in your .env. That’s it.
If you were maintaining a custom build for other reasons, note that the UID and GID build arguments have been removed from the Dockerfile. They are no longer needed.
From BorgWarehouse v2 to v3
Breaking changes that needs your attention
- API has been full rewrite to be REST compliant and idempotent
- Check the new API documentation and update your configurations if you use integrations.
- repository’s name is now used instead of id to identify repository in all API.
- You need to update your cron service to trigger the new API for storage and status update :
/api/cronjob/checkStatus»/api/v1/cron/status/api/cronjob/getStorageUsed»/api/v1/cron/storage
From BorgWarehouse v1 to v2
What’s new in v2 ?
Core changes :
- One unix user is now used for all backup. BorgWarehouse now complies with BorgBackup documentation recommendations.
sudopermissions is now no longer required at all.- Repositories are now all in the same root folder.
- Inside the
repo.json, therepositorykey is not use anymore. TherepositoryNamekey is new and now match the exact name of the repository where are stored datas for this repository. - All SSH public key must be unique (see why here or here). BorgWarehouse will warn you if you try to use an already use one.
- All environment variables are now runtime variables (buildtime variables have been removed for docker compatibility).
Features :
- 100% ready for Docker 🚀
- Add robots.txt to default installation.
- History of
repo.jsonfile is now logged inconfig/versionsfolder.
Fixes :
- Don’t try to stat repositories if there is no repositories created in BorgWarehouse
- Improvement for SMTP error log
- Add prettier config file
- better description in Wizard’step 2
How to migrate from v1 to v2 ?
It’s a big update, and there is no automatic migration process. I will try to explain the steps to follow to migrate from v1 to v2 soon.