Ente is one of those projects where “self-hosted photos app” undersells the scope.

The repo is a full end-to-end encrypted cloud platform: Photos, Auth, Locker, public albums, sharing, mobile apps, desktop clients, web apps, a Go API server, object storage integration, and shared Rust cryptography bindings.

That makes Ente more ambitious than a simple gallery. It also makes it a more serious self-host.

What is Ente?

Ente is an AGPL-3.0, fully open source, end-to-end encrypted cloud platform.

The main apps today are:

  • Ente Photos: encrypted photo backup, gallery, sharing, face detection, semantic search, and import/export workflows.
  • Ente Auth: an open source encrypted authenticator app and Authy alternative.
  • Ente Locker: encrypted storage for documents, credentials, notes, and emergency-access style workflows.

I analyzed commit:

63dcf1729950e6fbb42704c75d5356d0c3c3928f

At the repository level, Ente is mostly a Dart/Flutter, TypeScript/React/Next, Go, and Rust project.

Why Self-Host It?

The reason to care about Ente is simple: it is built around client-side encryption.

With a normal gallery or document app, the server can generally see a lot. With Ente, the design pushes sensitive work to the clients. The server stores and coordinates encrypted data, while clients handle encryption, decryption, keys, and user-facing workflows.

That does not make operations disappear. You still need backups, TLS, upgrades, storage planning, and recovery testing. But it does change the trust model.

Good reasons to self-host Ente:

  • you want private photo backup without a generic cloud account;
  • you want Auth, Photos, and Locker tied to one encrypted platform;
  • you want the server code, clients, and cryptographic bindings visible;
  • you are comfortable running PostgreSQL and S3-compatible storage;
  • you want something closer to a product platform than a hobby gallery.

Less ideal reasons:

  • you want the simplest possible NAS photo viewer;
  • you want server-side image/video processing to be the main feature;
  • you do not want to manage secrets, database backups, object storage, or HTTPS.

Architecture

The server is called Museum. It is a Go API service that powers Ente’s apps.

The core self-hosted deployment has four moving parts:

  • Museum: the API on port 8080.
  • PostgreSQL: relational state for users, collections, sessions, sharing, billing-like records, metadata, and application state.
  • S3-compatible storage: encrypted file/object storage. The local quickstart uses MinIO.
  • Web apps: a single ghcr.io/ente/web image serving Photos, Accounts, Albums, Auth, Cast, Share, Embed, Paste, Locker, and Memories.

The web image serves apps on separate ports:

3000 Photos
3001 Accounts
3002 Public albums
3003 Auth
3004 Cast
3005 Share
3006 Embed
3008 Paste
3009 Locker
3010 Memories

The server API exposes a broad product surface: users, sessions, passkeys, 2FA, SRP login flows, recovery keys, files, upload URLs, thumbnails, previews, video data, collections, public albums, sharing links, cast devices, family features, emergency contacts, legacy kits, billing, and more.

Tech Overview

The monorepo is large, but the self-hosting path is understandable:

  • Backend: Go, Gin, PostgreSQL, S3, Prometheus, WebAuthn, Stripe integrations, cron jobs.
  • Web: React/Next apps built with Node 24 and served by Nginx.
  • Mobile/Desktop: Flutter and native platform packages.
  • Crypto/native layer: Rust bindings for web, mobile, desktop, and Node.
  • Storage: PostgreSQL plus S3-compatible object storage.
  • Container images: ghcr.io/ente/server and ghcr.io/ente/web.

The web Dockerfile installs the Rust wasm32-unknown-unknown target during build, builds product-specific Rust/WASM packages, builds the web apps, and copies the final static assets into Nginx.

That means the web UI is not just a plain HTML frontend. It ships real client-side logic, including WebAssembly used by the encrypted apps.

Is There a UI?

Yes.

Ente has multiple UIs:

  • web apps through the ghcr.io/ente/web container;
  • iOS and Android clients;
  • F-Droid builds;
  • desktop clients for Linux, macOS, and Windows.

For self-hosting, the web image is the easiest UI surface to start with. Photos is exposed on port 3000, Auth on 3003, and Locker on 3009.

Self-Hosting with Docker

Upstream provides a quickstart script that generates a local Docker Compose stack and a museum.yaml file with random secrets.

For a reusable Home-Lab style setup, I made a public snippet that keeps secrets out of Git and follows the same shape:

  • Museum API;
  • Postgres;
  • MinIO;
  • MinIO bucket initialization;
  • Ente web apps;
  • socat bridge for the local MinIO endpoint expected by Museum.

The reusable Home-Lab compose file is here:

The site can include the same snippet from the Home-Lab submodule:

assets/snippets/ente/docker-compose.yml

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: ente
      POSTGRES_PASSWORD: entepassword
      POSTGRES_DB: ente
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    restart: unless-stopped

  ente:
    image: enteio/ente:latest
    depends_on:
      - postgres
      - redis
    ports:
      - "8080:80"
    environment:
      - DATABASE_URL=postgresql://ente:entepassword@postgres:5432/ente?sslmode=disable
      - REDIS_URL=redis://redis:6379
      - ENCRYPTION_KEY=your-secure-encryption-key-here
      - JWT_SECRET=your-jwt-secret-here
    volumes:
      - ente_data:/data
    restart: unless-stopped

volumes:
  postgres_data:
  ente_data:

Create the local config files:

cd assets/snippets/ente
cp .env.sample .env
cp museum.yaml.sample museum.yaml

Edit both files before first boot.

Generate strong values for the database password, MinIO credentials, Museum encryption key, Museum hash key, and JWT secret. For local generation, the upstream quickstart uses random byte output roughly equivalent to:

openssl rand -base64 32
openssl rand -base64 64
openssl rand -base64 32 | tr '+/' '-_'

Start the stack:

docker compose up -d

Then open:

http://localhost:3000  Photos
http://localhost:3003  Auth
http://localhost:3009  Locker
http://localhost:8080  Museum API
http://localhost:3201  MinIO console

For anything beyond a local laptop test, put the apps behind HTTPS and make sure the API origin and app URLs match the public domains you actually use.

Static Deployment Notes

The Ente web image serves built web apps from Nginx, and it can inject ENTE_API_ORIGIN at container startup.

So can the UI be served like static files? Mostly yes.

But “static UI” is not the same thing as “full app without a backend.” Full Ente functionality needs:

  • a reachable Museum API;
  • PostgreSQL behind Museum;
  • S3-compatible object storage;
  • app endpoint configuration in museum.yaml;
  • correct CORS/API origin behavior;
  • HTTPS for real-world browser use.

This is different from a small PWA where all work happens in the browser. Ente’s browser apps do important client-side encrypted work, but they still coordinate with the server.

Does Ente Use WASM?

Yes.

The web build compiles Rust packages to WebAssembly. The rust/ tree contains web bindings for several Ente surfaces, including Photos, Auth, Locker, Paste, Cast, prelogin, legacy flows, and related product logic.

That matters because Ente’s trust model depends on clients doing sensitive work locally. WASM is part of how the web apps share lower-level implementation across products and platforms.

Field Test

I smoke-tested the published web image:

docker run -d --name ente-web-foss-trial \
  -e ENTE_API_ORIGIN=http://127.0.0.1:18080 \
  -p 127.0.0.1:13000:3000 \
  -p 127.0.0.1:13003:3003 \
  -p 127.0.0.1:13009:3009 \
  ghcr.io/ente/web:latest

Results:

  • Photos returned HTTP 200.
  • Auth returned HTTP 200.
  • Locker returned HTTP 200.
  • Nginx served all three apps and the container startup script applied the configured API origin.

I also checked the published server and web image manifests. Both images are available from GHCR for linux/amd64 and linux/arm64.

The full Ente Compose stack was not kept running on this host because the local Docker daemon has already exhausted its default bridge address pools during previous multi-container trials. The reusable Compose file was still validated with docker compose config.

Operational Caveats

Ente deserves more care than a casual weekend container.

Back up PostgreSQL and object storage together. Keep museum.yaml safe. Test restore procedures. Do not rotate encryption keys casually after storing data. Do not expose MinIO publicly unless you know exactly why. Put the public app/API surfaces behind TLS.

If you are comparing it with Immich, PhotoPrism, or Nextcloud Memories, focus on the trust model. Ente prioritizes end-to-end encryption and multi-client product polish. That can be a better fit for private cloud users, but it also means some server-side photo-management assumptions do not map one-to-one.

FAQ

Is Ente fully open source?

The repository is AGPL-3.0 and contains the server, web apps, mobile apps, desktop clients, and shared cryptography/native code.

Is there a web UI?

Yes. The ghcr.io/ente/web image serves Photos, Accounts, Albums, Auth, Cast, Share, Embed, Paste, Locker, and Memories on separate ports.

Can I use it only for Ente Auth?

You can focus on Auth, and the server is designed to be data-agnostic. If you do not use Photos or Locker, the storage requirements may be lighter, but you still need the API and account flows.

Can I deploy only the web app to Cloudflare Pages?

You can host the web assets statically in principle, but full functionality still needs Museum, PostgreSQL, and S3-compatible storage. For a normal self-hosted setup, the Docker web image is simpler because it already packages all apps and injects ENTE_API_ORIGIN at startup.

Does it use WASM?

Yes. Ente builds Rust packages to WebAssembly for the web apps.

Is it a drop-in Google Photos replacement?

It can cover photo backup, galleries, sharing, imports, exports, and encrypted storage. But the self-hosted version should be treated as an advanced deployment, not as a zero-maintenance consumer cloud.

What should I protect first?

Protect museum.yaml, PostgreSQL backups, MinIO/object storage data, and the public API/app surfaces. Without consistent backups across all of those, a restore may not be useful.

Conclusion

Ente is impressive because it is not just a photo viewer with a privacy label. It is a serious encrypted application platform with multiple clients and product surfaces.

That scope is exactly why the self-hosting story is more demanding.

If you want a private encrypted cloud for photos, authenticator codes, documents, and sharing, Ente is worth studying. Just approach it like infrastructure: generate real secrets, use HTTPS, back up everything, test restores, and read the upstream self-hosting notes before upgrades.