Ep
Storage Azure

Ente Photos on Ubuntu 24.04 on Azure User Guide

| Product: Ente Photos on Ubuntu 24.04 LTS on Azure

Overview

This image runs Ente Photos, the open source, fully end-to-end-encrypted photo storage and backup platform, on Ubuntu 24.04 LTS. Every photo, video and its metadata is encrypted on your device before it is uploaded, so the server only ever stores ciphertext - no one, including you as the operator, can read the library without your password. The same self-hosted server works with the Ente web, mobile (iOS/Android) and desktop apps, giving you automatic encrypted backup from every device.

Ente self-hosts as a small set of services, orchestrated by Docker Compose and managed by systemd:

  • museum - the Ente API server (Go), listening on :8080
  • web - the Photos web app, on :3000
  • postgres - PostgreSQL 15, holding the account, collection and file metadata (never exposed off the instance)
  • minio - an S3-compatible object store, holding the encrypted photo/video blobs, on :3200

nginx on port 80 serves the Photos web app at /, proxies the API at /api/, and answers an unauthenticated /healthz for load-balancer probes. Because the client uploads encrypted blobs directly to the object store, the object store is published on port 3200.

What is included:

  • The full Ente self-host stack (museum, web, PostgreSQL, MinIO) run under Docker Compose via an ente.service systemd unit, from images pinned by digest
  • The Photos web app and API on :80 behind nginx, and the encrypted object store on :3200
  • Every secret generated fresh per-VM on first boot - the PostgreSQL password, the object-store access and secret keys, and the three museum master secrets (encryption key, hash key, JWT secret) - so no baked default survives into the image
  • No server-side admin password: because Ente is end-to-end encrypted, you create your account from the app and your keys are generated on your device
  • PostgreSQL and the encrypted blob store on a dedicated Azure data disk at /var/lib/ente, captured into the image
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for large libraries or many devices. NSG inbound: allow 22/tcp from your management network, and 80/tcp (web app + API) and 3200/tcp (encrypted object storage) from your clients. Both port 80 and port 3200 must be reachable from every device you upload from, because the client uploads encrypted blobs directly to the object store.

Assign a static public IP. The upload endpoint is derived from the VM's public IP on first boot and embedded in the object-store URLs the client uses. If the IP later changes, uploads break until the endpoint is refreshed. Assigning a static public IP avoids this.

Ente serves plain HTTP here. For production, put your own domain and TLS in front of both the web app and the object store.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Ente Photos by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22). After creation, also open 80 and 3200 (Step 2 shows the CLI equivalent). Review the dedicated data disk on the Disks tab, then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name ente \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name ente --port 80   --priority 1010
az vm open-port --resource-group <your-rg> --name ente --port 3200 --priority 1020

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

On the very first boot, ente-firstboot.service generates every per-VM secret, sets the public-IP endpoints, brings the Compose stack up on fresh volumes and installs the object-store network rule. On a fresh VM this takes a minute or two.

Step 4 - Confirm the services are running

Ente runs as a Docker Compose stack behind nginx. Confirm the systemd units are active and the four containers are up:

systemctl is-active docker nginx ente
sudo docker compose -f /opt/ente/compose.yaml ps

Expected output:

active
active
active
NAME              SERVICE    STATUS                        PORTS
ente-minio-1      minio      Up About a minute             0.0.0.0:3200->3200/tcp
ente-museum-1     museum     Up About a minute (healthy)   127.0.0.1:8080->8080/tcp
ente-postgres-1   postgres   Up About a minute (healthy)   5432/tcp
ente-web-1        web        Up About a minute             127.0.0.1:3000->3000/tcp

Ente services and containers running

The unauthenticated health endpoint and the museum API should both answer:

curl -s http://localhost/healthz
curl -s http://localhost:8080/ping
ok
{"id":"0137a0c754ac0fe4f2c4c7421727c349327eb990","message":"pong"}

Ente health and API ping

Step 5 - Retrieve the per-VM secrets

Every secret is generated fresh on this VM's first boot and written to a root-only file. There is no admin password (Ente is end-to-end encrypted - your account is created from the app), but the object-store and database secrets, the server URL and the account-creation steps are all recorded here:

sudo cat /root/ente-credentials.txt
# Ente Photos self-host - generated on first boot by ente-firstboot.service.
# These per-VM secrets are unique to THIS VM. Store them somewhere safe.

ENTE_URL=http://20.55.x.x/
ENTE_MINIO_ACCESS_KEY=minio-user-HHm4Cp62
ENTE_MINIO_SECRET_KEY=zhb8nfls****************  (unique per VM)
ENTE_DB_PASSWORD=/Iw5abXpr****************  (unique per VM)

# HOW TO CREATE YOUR ACCOUNT
# Ente is end-to-end encrypted, so there is NO server-side admin password: you create
# your account from the web app and your keys are generated on your device...

Per-VM Ente credentials file

Step 6 - Create your end-to-end-encrypted account

Open the Photos web app in your browser at http://<vm-public-ip>/ and choose Sign up. Enter your email and a password - the password derives your encryption key on your device, so keep it safe; it cannot be recovered by the server.

Ente sends a 6-digit verification code by email. This appliance does not send email, so the code is written to the server logs instead. Retrieve it with:

sudo docker compose -f /opt/ente/compose.yaml logs museum | grep -i "verification code" | tail -1
INFO email.go:111 Skipping sending email to you@example.com: Verification code: 140076

Enter that code in the web app to finish creating your account.

Ente sign-up and verification

Step 7 - Sign in and upload your first photos

After verification you land in your encrypted library.

Ente Photos library after login

Click Upload and choose Files (or Folder, or drag and drop into the window) to add photos and videos. Each item is encrypted in your browser before it leaves your device and stored as an opaque blob in the object store on port 3200 - the server only ever holds ciphertext.

Ente upload dialog - Files, Folder or drag-and-drop

Step 8 - Connect the mobile and desktop apps

Install the official Ente Photos app (iOS, Android, macOS, Windows, Linux). On the sign-in screen, open the developer/self-host option and set the endpoint to your server, then sign in with the same account:

http://<vm-public-ip>/api

The mobile apps can then back up your camera roll automatically - encrypted end to end, to storage you control.

Health checks and troubleshooting

  • Health probe: curl -s http://<vm-public-ip>/healthz returns ok.
  • Restart the stack: sudo systemctl restart ente
  • View server logs: sudo docker compose -f /opt/ente/compose.yaml logs museum
  • Uploads fail from a phone/other machine: confirm port 3200 is open in your NSG, and that the public IP has not changed since first boot (the object-store endpoint is pinned to the boot-time IP - use a static IP).

Support

This image is maintained by cloudimg with 24/7 support. The Ente project is developed at github.com/ente-io/ente and is licensed under the GNU Affero General Public License v3.0.