Open Source • Self-Hosted • Privacy-First
Your Photos. Your Keys. Your Album Tree.
OpenPhotos is a self-hosted photo platform with locked albums (E2EE), nested albums, AI-powered discovery, and resumable uploads.
Demo
Access the demo here: https://demo.openphotos.ca
For the mobile app, use https://demo.openphotos.ca for the Server Endpoint URL.
Download the iOS app from the App Store: OpenPhotos
Login Credentials
| Password | |
|---|---|
demo@openphotos.ca |
demo |
What's Included
- Rust server binaries:
openphotos+ TUS sidecarrustus - Static web app:
web-photos - Android app source:
android-java - Android release helper:
scripts/build_android_installer.sh
Download Models (Before Build)
GitHub source uploads do not include large model binaries. Download runtime models first:
This populates required runtime files under models/ (CLIP + face models).
Build Server (No Installer)
macOS (native build)
cargo build --release --no-default-features --bin openphotos cargo build --release --manifest-path rustus/Cargo.toml --bin rustus
Build outputs:
target/release/openphotosrustus/target/release/rustus
Linux (Docker cross-build via script)
DOCKER_IMAGE="${DOCKER_IMAGE:-rust:1-bookworm}" USE_LIBHEIF=0 ./start.sh --build-linux --linux-target x86_64-unknown-linux-gnu DOCKER_IMAGE="${DOCKER_IMAGE:-rust:1-bookworm}" USE_LIBHEIF=0 ./start.sh --build-linux --linux-target aarch64-unknown-linux-gnu
Build outputs:
dist/linux/x86_64-unknown-linux-gnu/openphotosdist/linux/x86_64-unknown-linux-gnu/rustusdist/linux/aarch64-unknown-linux-gnu/openphotosdist/linux/aarch64-unknown-linux-gnu/rustus
Windows (Docker cross-build via script)
./start.sh --build-windows --windows-target x86_64-pc-windows-msvc
Build outputs:
dist/windows/x86_64-pc-windows-msvc/openphotos.exedist/windows/x86_64-pc-windows-msvc/rustus.exe
Build Web Client
Build output:
web-photos/out
Build Android App
The exported source includes the Android project under android-java.
Before building, make sure the Android SDK is discoverable either by:
- setting
ANDROID_HOMEorANDROID_SDK_ROOT, or - creating
android-java/local.propertieswithsdk.dir=/absolute/path/to/Android/sdk
The export intentionally does not include android-java/local.properties because that file is machine-specific.
Build a debug APK:
cd android-java
./gradlew :app:assembleDebugBuild output:
android-java/app/build/outputs/apk/debug/app-debug.apk
Optional release APK helper:
scripts/build_android_installer.sh
The helper tries ANDROID_HOME / ANDROID_SDK_ROOT first, then common Android SDK install locations, and writes android-java/local.properties automatically when needed.
It always produces an installable signed release APK.
If ANDROID_KEYSTORE_* env vars are not provided, it auto-generates and reuses:
android-java/.openphotos-signing/openphotos-auto-release.jks
Back up that keystore file. Future app updates must use the same signing key.
Build output:
dist/android-packages/openphotos-android-release.apk
Start Servers (macOS, Linux, Windows)
Run these from the repository root after models are downloaded.
macOS
Linux
Windows (PowerShell)
- Build binaries:
cargo build --release --locked --no-default-features cargo build --release --locked --manifest-path rustus/Cargo.toml --bin rustus
- Start Rustus (Terminal 1):
$env:RUSTUS_SERVER_HOST="127.0.0.1" $env:RUSTUS_SERVER_PORT="1081" $env:RUSTUS_URL="/files" $env:RUSTUS_DATA_DIR="$PWD\data\uploads" $env:RUSTUS_INFO_DIR="$PWD\data\uploads" $env:RUSTUS_TUS_EXTENSIONS="creation,termination,creation-with-upload,creation-defer-length,concatenation,checksum" $env:RUSTUS_MAX_BODY_SIZE="52428800" $env:RUSTUS_HOOKS="pre-create,post-finish" $env:RUSTUS_HOOKS_FORMAT="v2" $env:RUSTUS_HOOKS_HTTP_URLS="http://127.0.0.1:3003/api/upload/hooks" $env:RUSTUS_HOOKS_HTTP_PROXY_HEADERS="Authorization,X-Request-ID,Cookie" $env:RUSTUS_LOG_LEVEL="INFO" New-Item -ItemType Directory -Force -Path "$PWD\data\uploads" | Out-Null .\rustus\target\release\rustus.exe
- Start OpenPhotos (Terminal 2):
.\target\release\openphotos.exe --model-path models --database data --log-level info
Health checks:
http://127.0.0.1:3003/ping(OpenPhotos API)http://127.0.0.1:1081/health(Rustus)