Selfhost Ente Photo's CLI Backup
Note: This post is not about self-hosting an Ente Photo’s storage server (museum). This post is about self-hosting automated backups of your Ente Photo’s using cron and the Ente Photo’s CLI Docker container.
Ente Photos is an open-source end-to-end encrypted photo application that includes some important features like, phone camera sync and facial, place, and object recognition via a private local ML (i.e. AI).
It’s essentially a better, safer, and more private alternative to Dropbox Camera Uploads or Google Photos.
Although it’s a FOSS project, the primary reason people pay for an Ente Photos subscription is their storage service. Your photos are encrypted and stored in several data centres for backup - you don’t need to run your own server.
Exporting Ente Photos for Backups
Ente Photos takes great care with your data, and they are quite open about how secure the files are with their redundant locations.
However, I like to keep a copy of my own data. Especially significant things like photos.
You can use the Electron-based desktop application on most OSs (including Linux) to automatically export your photos incrementally onto your local hard-drive unencrypted (while the app is running), I still prefer to run this on my homeserver/NAS which has a lot more storage space, RAID and snapshot backups already.
Ente Photos CLI Docker Backup via Cron
Although the Ente software is open-source and can be reviewed on GitHub. At the time of writing they do not publish Docker containers to public registries as one might hope (see discussion #711).
And so… we need to build our own Ente CLI container using their instructions to then run it on our NAS or homeserver.
Unfortunately, Ente also uses a monorepo (all their software projects are under one huge git repository). It’s not simple to clone specific parts of a repo, so we’ll need to close the whole thing at (500MiB+).
Note: Be sure to update the code below to include your own data paths. I’ve used example directories within your users home folder, but you’ll likely have your own locations setup on a NAS/homeserver.
Create a directory for your Ente Photos CLI docker project:
mkdir -p ~/cron-ente-photos/
Then we need to clone the entire Ente repo inside.
cd ~/cron-ente-photos/
git clone https://github.com/ente-io/ente.git
Then create your Docker compose.yaml
file with your config.
nano compose.yaml
services:
cron-ente-cli:
container_name: cron-ente-cli
restart: on-failure
build:
context: ente/cli/ # This points into the cloned repo to the cli project.
command: /bin/sh
volumes:
# This is mandatory to mount the local directory to the container at /cli-data
# CLI will use this directory to store the data required for syncing export
- ~/container-configs/ente-cli:/cli-data:rw
- ~/archives/ente-photos:/data:rw
stdin_open: true
tty: true
networks: {}
# Details: https://benhoskins.dev/ente-photos-export-docker/
# To add an Ente account/login:
# `docker exec -it cron-ente-cli /bin/sh -c "./ente-cli account add"`
# To run other ente-cli commands (https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente.md):
# `docker exec -it cron-ente-cli sh`
# To run export and exit:
# `COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f ~/cron-ente-photos/compose.yaml run cron-ente-cli /bin/sh -c "./ente-cli export"`
Make sure these volume paths exist before running the container.
Once setup, we’ll run this container’s export command via cron from the host. Each time an export finishes, it’ll exit the container. So we won’t have a long-lived ongoing running container sitting around doing nothing. This is the main reason we use run
over exec
.
A note on “docker compose” vs “docker-compose”. I use the docker engine that’s distributed through the Debian repositories, rather than the latest and greatest version directly from the Docker repos. Therefore, I don’t have access to docker compose
(which has BuildKit enabled) and only have access to the older docker-compose
(which does not enable BuildKit by default).
The Ente CLI docs state that Ente CLI requires BuildKit to build the docker image. You can see that I’ve added COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1
in-front of the “run export” command here. If you’re using a newer version of Docker you don’t need this, and should remove the hyphen from “docker-compose”.
You can find the Ente CLI repo folder here, and the Ente CLI docs here. My compose file above is based on their example here.
Next we login to Ente Photos inside this container:
DOCKER_BUILDKIT=1 docker-compose run ente-cli /bin/sh -c "./ente-cli account add"
This will initially build the container from the cloned source code, start it, drop you into it’s shell and then run the Ente CLI login command.
Follow the steps to add an account, e.g. username, password and 2FA.
It will also ask you for a directory path. You’ll see in the compose.yaml
above that we mounted “/data” within the container to our hosts “~/archives/ente-photos”. So in the Ente path, you could use something like “/data/ben”. All your photos will be exported to your host’s folder “~/archives/ente-photos/ben” (subdirectories are the albums etc).
Next we create a bash script to call from cron on the host:
nano ./backup-ente-photos-export.sh
chmod +x ./backup-ente-photos-export.sh # make executable
#! /bin/bash
# v0.1 2025-01-15
# https://benhoskins.dev/ente-photos-export-docker/
# Check if another instance of this script is already running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f ~/cron-ente-photos/compose.yaml run cron-ente-cli /bin/sh -c "./ente-cli export"
And finally we’ll set a cron job to run the script often:
crontab -e
Then add the following job to the bottom:
22 0 * * * ~/backup-ente-photos-export.sh > /dev/null 2>&1
This will run the export every day at 22mins past midnight!
Now you have an automated export of Ente Photos using their CLI docker container. Let me know how you get on!
Comments & Questions
Reply by email to send in your thoughts.
Comments may be featured here unless you say otherwise. You can encrypt emails with PGP too, learn more about my email replies here.
PGP: 9ba2c5570aec2933970053e7967775cb1020ef23