Ente for your Photos: self-hosted, encrypted, unlimited (and on docker)

docker synology photos self-hosted ente encrypted unlimited replacement alternative google private privacy
Photo by ian dooley / Unsplash

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.

Android app menu

Prerequisites

Before we get started, you'll need the following:

  1. 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)
  2. The ability to SSH / use CLI/terminal on your machine
  3. Some sort of access and relevant permissions to manage and create new directories and files, and edit them
  4. 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.yml
    • credentials.yaml
    • minio-provision.sh
    • museum.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.yml and 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-v3

Note 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-v3

We 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 -d to create a stack called ente
  • 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.

📳
I couldn't enter developer mode on the Windows desktop app. I've only tested the Google Play Android app so far, so that's what the rest of this will focus on
  • Once downloaded and installed, open the app
The Android home screen

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)
if you're using a reverse proxy and have already set up the routing, you can use your https://ente.mydomain.com here as well

This 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.yaml also, 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-museum

sudo is necessary if your user doesn't have docker permissions already

  • Review your logs, searching the lines beginning INFO for something that looks like your device, and ending with user_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.yaml again, and add this in to internal.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.yaml are applied
⚠️
This isn't just a restart, the container must be totally recreated. In SSH, this can be achieved with the command 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
🪟
As I'm using Windows, I'll be moving forward with that from this point in the guide, but the steps should be the same for other systems too.
  • 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.yaml inside that same directory
  • Open config.yaml and 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 necessary

This 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+R then type cmd.exe into the field and hit Enter
  • Navigate to your ente directory
  • Test the executable works by typing ente account list. It should return Configured accounts: 0
If it doesn't work, double check the API credentials in the config.yaml and make sure it's in the same folder as your ente.exe

We'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.com

the 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.


Getting the most out of docker-compose: tips and tricks
A list of handy tips you can implement immediately when creating your docker compose files
Preshared Keys and more - SSH on Linux and Synology
An easy guide which explains how to access and use your SSH sessions to their fullest, including clients and logging in with preshared keys
Portainer - Easy Container Management for Docker
A step-by-step docker walkthrough to installing and configuring Portainer, your one-stop container-management resource