Ente for your Photos: self-hosted, encrypted, unlimited (and on docker)
Google Photos, Apple Photos; the original(s) and still arguably the best. High availability, fast speeds. They. Just. Work.
For years, home-labbers have been trying to search out (and create when they have the skills and time) self-hosted services that could replace the big two, and finding out just how big an ask and task that is. Whether it's face/object/place recognition, memories, geo-tracking or something else, it all requires capabilities that a lot of home servers don't necessarily have, and which devs have to try and figure out how to cram into underpowered setups.
Ente (ente.io, Github Repo) is one of these. With a focus on privacy and encryption, they have online, paid-for services which include photos and 2Fauth, but they have kindly made them available so you can self-host for free, with apps for Android, iPhone and Desktop. I'm going to show you how to set up photos with unlimited storage and backups from your phone using prebuilt docker images.

Prerequisites
Before we get started, you'll need the following:
- Docker and docker-compose installed on your machine, and familiarity with docker-compose and the moving parts (check out the Docker page on this site for some introduction/advanced concepts if you need to)
- The ability to SSH / use CLI/terminal on your machine
- Some sort of access and relevant permissions to manage and create new directories and files, and edit them
- If you plan to access this remotely without a VPN to your network, then you will need a reverse proxy already set up on its own docker network (for instance called
proxy) with the relevant SSL certificate(s) (check out my SWAG article for some info on how to do that if you need some help)
The Docker Containers
We can set up our containers in a single stack, which is great, however there are a number of other supporting files we need to create too.
The docker-compose.yml
Let's start easy.
- Create your root directory for this project (say
/volume1/docker/ente). Everything from now on will go in there - Create the following files:
docker-compose.ymlcredentials.yaml
minio-provision.shmuseum.yaml(we won't touch this during the setup, but you may need it if you decide to use more of the functionality than we'll go through today)- Open your
docker-compose.ymland paste the following into it, modifying where indicated, then save:
networks:
default:
name: ente
ipam:
config:
- subnet: 172.XX.X.0/27 #change Xs as necessary to select an unused subnet
#uncomment the below and change the name 'proxy' to the name of your reverse proxy network if necessary
#proxy:
#external: true
services:
ente-museum:
image: ghcr.io/ente-io/server
container_name: ente-museum
ports:
- 8080:8080 #change port number before the `:` as necessary to avoid port clashes
depends_on:
ente-postgres:
condition: service_healthy
environment:
ENTE_CREDENTIALS_FILE: /credentials.yaml
volumes:
- custom-logs:/var/logs
- ./museum.yaml:/museum.yaml:ro
- ./credentials.yaml:/credentials.yaml:ro
#uncomment the below if you plan to use a reverse proxy
# networks:
# - proxy #change the name to your reverse proxy network
# - default
ente-socat:
image: alpine/socat
container_name: ente-socat
network_mode: service:ente-museum
depends_on:
- ente-museum
command: "TCP-LISTEN:3200,fork,reuseaddr TCP:ente-minio:3200"
ente-postgres:
image: postgres:12
container_name: ente-postgres
ports:
- 5432:5432 #change port number before the `:` as necessary to avoid port clashes
environment:
POSTGRES_USER: pguser #change
POSTGRES_PASSWORD: pgpass #change
POSTGRES_DB: ente_db
healthcheck:
test:
[
"CMD",
"pg_isready",
"-q",
"-d",
"ente_db",
"-U",
"pguser" #change if you have changed `POSTGRES_USER` above
]
interval: 1s
timeout: 5s
retries: 20
volumes:
- postgres-data:/var/lib/postgresql/data
ente-minio:
image: minio/minio
container_name: ente-minio
ports:
- 3200:3200 # API #change port number before the `:` as necessary to avoid port clashes
- 3201:3201 # Console #change port number before the `:` as necessary to avoid port clashes
environment:
MINIO_ROOT_USER: test #change
MINIO_ROOT_PASSWORD: testtest #change
command: server /data --address ":3200" --console-address ":3201"
volumes:
- minio-data:/data
ente-minio-provision:
image: minio/mc
container_name: ente-minio-provision
depends_on:
- ente-minio
volumes:
- ./minio-provision.sh:/provision.sh:ro
- minio-data:/data
entrypoint: sh /provision.sh
volumes:
custom-logs:
postgres-data:
minio-data:There's a lot going on up there, make sure to review the ports, usernames and passwords. If you change them then take a note, as we'll need them later on.
credentials.yaml
- Open this file, copy paste the following, then save:
#this removes the need to set up an email server.
internal:
hardcoded-ott:
emails:
- "your@email.com,123456" #change to the email you plan to sign up with, and choose any 6-number string you want
admins:
- #a string we will come back to later
db:
host: ente-postgres
port: 5432
name: ente_db
user: pguser #change if changed in the docker-compose.yml
password: pgpass #change if changed in the docker-compose.yml
#all keys, secrets and endpoints below should be changed as necessary to reflect the minio user, password and mapped port
#all endpoints should be changed to your machine's IP on your LAN network, and the mapped port
s3:
are_local_buckets: true
b2-eu-cen:
key: test #change
secret: testtest #change
endpoint: machineIP:3200 #change
region: eu-central-2
bucket: b2-eu-cen
wasabi-eu-central-2-v3:
key: test #change
secret: testtest #change
endpoint: machineIP:3200 #change
region: eu-central-2
bucket: wasabi-eu-central-2-v3
compliance: false
scw-eu-fr-v3:
key: test #change
secret: testtest #change
endpoint: machineIP:3200 #change
region: eu-central-2
bucket: scw-eu-fr-v3Note the instructions in this yaml block after # above
minio-provision.sh
- Open this file, save then close:
#!/bin/sh
# Script used to prepare the minio instance that runs as part of the development
# Docker compose cluster.
while ! mc config host add h0 http://ente-minio:3200 test testtest
do
echo "waiting for minio..."
sleep 0.5
done
cd /data
mc mb -p b2-eu-cen
mc mb -p wasabi-eu-central-2-v3
mc mb -p scw-eu-fr-v3We don't need to change anything here or access it again
Creating the containers
- Using CLI / SSH, or Portainer, spin up your stack (in SSH, navigate to your root ente folder, then type
sudo docker-compose -p "ente" up -dto create a stack calledente - The compose will create 5 containers, 3 volumes and 1 network which the containers will be attached to
- Follow their logs to make sure all is good and they don't throw any errors
We'll come back to the credentials.yaml in a bit, but for now we're going to move to the app.
Setting up the app and getting your user ID
The folks at Ente have made this super easy for you. Whatever your OS flavor, there's something official you can download and use, easily.
- Head over to https://github.com/ente-io/ente#ente-photos and download your preferred app(s). The options are AppStore, Google Play (Android), F-Droid (Android), desktop or web
- Once downloaded and installed, open the app

We need to set the app to communicate with the server we just set up in docker:
- Tap the screen 7 times, then click 'Yes' to head into developer settings

- Type in your server's IP on your LAN, and the published port (if you didn't change anything in the compose, this will be 8080)

https://ente.mydomain.com here as wellThis will connect, and then send you back to the first screen. You know it's worked because at the bottom it will now say that it's connected to the server at your IP.
- Click 'New to Ente' and sign in using the email you set in the
credentials.yaml - The app will ask you for an OTP. We set this in the
credentials.yamlalso, use the same one
The app will then begin to start up and go through its wizard for selecting albums on your phone for backup. Part of this will be to tell you you only have a free trial, and a max of 1GB of space. We have a little more to do to get this unlocked for you, so before you get carried away with backing up your entire world, let's get our storage space squared away.
Setting up Unlimited Storage
Head into the logs for your ente-museum container. Portainer is a great tool for this, otherwise you can access them in SSH with the following command:
sudo docker logs ente-museumsudo is necessary if your user doesn't have docker permissions already
- Review your logs, searching the lines beginning
INFOfor something that looks like your device, and ending withuser_id=and a 16-digit string. Something like the below:
INFO[41830]request_logger.go:100 func1 outgoing client_ip=172.22.0.1 client_pkg=io.ente.photos client_version=0.8.80 h_latency=4.015852ms latency_time=4.015852ms query=sinceTime=1714706794594040&source=bg req_body= req_id=5f547584-0e26-4e6b-8990-d02027e360ba req_method=GET req_uri=/collections/v2 status_code=200 ua=Dalvik/2.1.0 (Linux; U; Android 14; [phone model and OS build]) user_id=1234567890123456- Copy this user ID
- Open your
credentials.yamlagain, and add this in tointernal.admins. It should look something like this:
internal:
hardcoded-ott:
emails:
- "your@email.com,123456" #an email address and any 6 numbers
admins:
- 1234567890123456 #your user ID number which can be found in the logs- Save it, close it
- Recreate your containers so that the changes in the
credentials.yamlare applied
sudo docker-compose -p "ente" up -d --force-recreate- Check the logs again to make sure it's started up properly
Ente CLI
You have some options for this next bit, dependent on what machine/system/OS you want to use it on, but we need to download a binary now. You can read up on the CLI in their docs here, or you can go straight to here to select your binary to download:

- Click 'Assets', then from the drop-down click and download the binary which suits your OS

- Unpack the compressed folder you just downloaded
- Create a new directory (it doesn't matter where) on your machine, and move the unpacked executable file into it (windows is
ente.exe) - Create a new file called
config.yamlinside that same directory - Open
config.yamland paste the following, changing the API to your server's IP and ente-musuem API port:
endpoint:
api: http://192.100.10.5:8080 #change as necessaryThis is necessary so that the executable knows which server to connect to
- Open your machine's terminal or command prompt (or powershell). On Windows this is achieved by
WinKey+Rthen typecmd.exeinto the field and hitEnter

- Navigate to your
entedirectory

- Test the executable works by typing
ente account list. It should returnConfigured accounts: 0
config.yaml and make sure it's in the same folder as your ente.exeWe'll now add the account you registered on the app above.
- Type
ente account add - Follow the onscreen instructions, the export directory MUST be a directory on the machine you are using CLI on

We now need to run the admin command to upgrade your account to unlimited (remember how we changed the user_id in the credentials.yaml? That was for this to work).
- Once you've completed adding the account, type the following into the CLI, changing the email to your registered email:
ente admin update-subscription -a your@email.com -u your@email.comthe email for both -a and -u should be the same registered email
Success! Close down this terminal and head back into the photos app (maybe after a restart) and if you click the three lines top left of the screen, it should now display that you have a total of 102.4 TB available. This number doesn't reflect actual storage available on your server, but is a large enough number that most of us shouldn't reach it.
Final Thoughts
This isn't the most straight-forward of processes. Maybe at some point in the future the CLI will either be part of the museum image, or the commands we needed to use can be set in the credentials.yaml or the compose environment: block.
There's no face or object recognition that I could find (yet) and the search function (see screenshots below) doesn't seem to work for me yet 12 hours after uploading photos.




If I figure this out I'll post an update.
That aside, this app and server seem solid. Upload via LAN was smooth and worked in the background for Android where some don't, and the map function works.
Take some time to go through the functions and options on the app, explore the other apps as necessary. I would also recommend reviewing their documentation to see if there's anything else they offer (such as the 2FAuth) which you want to try.
Related Articles



