Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

37 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“Έ Immich Server

Self-hosted Google Photos alternative with machine learning, automated backups, and secure remote access.

Immich Cloudflare License

Immich Logo

✨ Features

Feature Description
πŸ–ΌοΈ Photo Management Upload, organize, and browse your photos with a beautiful UI
πŸ€– Machine Learning Facial recognition, object detection, and smart search
πŸ”’ Secure Access HTTPS everywhere via Cloudflare Tunnel + Caddy
πŸ’Ύ Auto Backups Scheduled PostgreSQL backups to NAS (7 daily, 4 weekly, 6 monthly)
πŸš€ One-Command Setup Interactive installer handles all configuration
☁️ Cloud Backup Per-user backups to Proton Drive via rclone

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Internet                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Cloudflare Tunnel                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β–Ό               β–Ό               β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  Caddy  β”‚    β”‚  Immich  │◄───│ Immich   β”‚
     β”‚  (TLS)  │───►│  Server  β”‚    β”‚    ML    β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β–Ό               β–Ό               β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Postgres β”‚   β”‚ Local SSD   β”‚  β”‚  NAS/NFS    β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ /srv/immich β”‚  β”‚ /mnt/photos β”‚
                  β”‚ (thumbnails,β”‚  β”‚ (originals, β”‚
                  β”‚  transcodes)β”‚  β”‚  backups)   β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                                          β”‚
                                   β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
                                   β”‚  DB Backup  β”‚
                                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Storage Layout

Data Location Why
Originals (library) NAS /mnt/photos/immich/library Large files, accessed less frequently
DB Backups NAS /mnt/photos/immich/backups Safe off-server backup
Thumbnails Local SSD /srv/immich/thumbs Fast access, regenerable
Transcoded Videos Local SSD /srv/immich/encoded-video Fast access, regenerable
Upload Buffer Local SSD /srv/immich/upload Fast writes during upload
Profiles Local SSD /srv/immich/profile Small files

πŸš€ Quick Start

git clone https://github.com/rustygreen/immich-server
cd immich-server
./scripts/install.sh

If you see Permission denied when running a script, either make scripts executable:

chmod +x scripts/*.sh

or run them via bash:

bash ./scripts/install.sh

The interactive installer prompts for your:

  • πŸ“§ Email (for Let's Encrypt certificates)
  • 🌐 Domain (e.g., photos.example.com)
  • πŸ”‘ Cloudflare API Token & Tunnel token
  • πŸ“‚ Storage locations

That's it! The script handles everything else automatically.


πŸ“‹ Prerequisites

Before running the installer, you'll need:

Requirement Details
πŸ–₯️ Linux Server Ubuntu/Debian recommended
🌐 Domain Managed by Cloudflare
πŸ”‘ Cloudflare API Token Create one β†’
πŸš‡ Cloudflare Tunnel Create one β†’

☁️ Cloudflare Setup

πŸ”‘ Cloudflare API Token
  1. Log in to Cloudflare Dashboard
  2. Go to My Profile β†’ API Tokens β†’ Create Token
  3. Use the Edit zone DNS template, or create a custom token with:
    • Permissions: Zone > DNS > Edit
    • Zone Resources: Include > Specific zone > your-domain.com
  4. Copy the token β€” you'll need it during installation
πŸš‡ Cloudflare Tunnel
  1. In Cloudflare Dashboard, go to Zero Trust β†’ Networks β†’ Tunnels
  2. Click Create a tunnel β†’ Select Cloudflared
  3. Name it (e.g., immich-server) and click Save tunnel
  4. On the connector setup page, select Docker and copy the token from the command:
    docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiYWJj...
    
    Copy just the token part (the long string after --token)
  5. Configure the public hostname:
    • Subdomain: photos
    • Domain: your domain
    • Service: http://immich:3001
  6. Save the tunnel
πŸ“‘ DNS Record (if not auto-created)
  1. Go to DNS β†’ Records for your domain
  2. Add a CNAME record:
    • Name: photos
    • Target: <tunnel-id>.cfargotunnel.com
    • Proxy status: Proxied (orange cloud)

πŸ’Ύ Storage Setup (Optional NFS)

Configure NAS/NFS Mount

If using a Synology NAS or other NFS share:

  1. Enable NFS on your NAS and create a shared folder (e.g., /volume1/photos)
  2. Set permissions to allow your server's IP with read/write access
  3. On your Linux server, edit the filesystem table:
    sudo nano /etc/fstab
  4. Add this line at the end (replace the IP and path with your NAS details):
    192.168.1.100:/volume1/photos  /mnt/photos  nfs  defaults,_netdev,nofail,soft,timeo=30,retrans=3,noatime  0  0
    

    ⚠️ The soft,timeo=30,retrans=3 options are important - they prevent system hangs if the NAS disconnects.

  5. Save and exit (Ctrl+X, then Y, then Enter)
  6. Mount the share:
    sudo mkdir -p /mnt/photos
    sudo mount -a

πŸ’‘ Using local storage only? Set UPLOAD_LOCATION=/srv/immich to keep everything on local SSD.


πŸ—„οΈ Storage Architecture

This setup uses split storage for optimal performance and reliability:

  • NAS (network): Original photos and backups β€” large files, accessed infrequently
  • Local SSD: Thumbnails and transcodes β€” frequently accessed, regenerable if lost

This prevents NAS connectivity issues from breaking the UI, while keeping your originals safely on network storage.

Local cache location: /srv/immich/ (created automatically by installer)


βš™οΈ Configuration

All configuration is done through the .env file. No need to modify any other files.

Variable Description
TZ Timezone (e.g., America/New_York)
DB_PASSWORD PostgreSQL password (auto-generated)
UPLOAD_LOCATION Where original photos are stored (NAS)
BACKUP_LOCATION Where database backups go (NAS)
ACME_EMAIL Email for Let's Encrypt certificates
DOMAIN Your photos domain (e.g., photos.example.com)
CF_API_TOKEN Cloudflare API token for DNS challenges
TUNNEL_TOKEN Cloudflare Tunnel token
ML_URL Machine Learning URL (leave unset for local ML)
ML_PORT Port to expose ML service (default: 3003)
ML_MEMORY Memory limit for ML container (default: 4G)
πŸ–₯️ Multi-Node Setup (Optional)

You can offload the ML container to a second machine to reduce load on your main server.

On Main Node (NUC 1):

  1. Edit .env and set ML_URL to point to your second node:

    ML_URL=http://192.168.1.101:3003
  2. Start only the main services:

    docker compose --profile main up -d

On ML Node (NUC 2):

  1. Clone the repo:

    git clone https://github.com/rustygreen/immich-server
    cd immich-server
  2. Create a minimal .env:

    echo "ML_PORT=3003" > .env
    echo "ML_MEMORY=4G" >> .env
  3. Start only the ML service:

    docker compose --profile ml up -d

Architecture with 2 nodes:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      NUC 1          β”‚         β”‚      NUC 2          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Immich Server │──┼────────►│  β”‚  Immich ML    β”‚  β”‚
β”‚  β”‚ Postgres      β”‚  β”‚  HTTP   β”‚  β”‚  (port 3003)  β”‚  β”‚
β”‚  β”‚ Redis         β”‚  β”‚         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  β”‚ Caddy         β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚  β”‚ Cloudflared   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Import Script Settings (Optional)

If you want to use the bulk import script (./scripts/import-photos.sh), add these to your .env:

Variable Default Description
IMMICH_API_KEY (required) Your Immich API key
IMPORT_DIR /mnt/photos/upload Source folder for imports
IMPORT_IMMICH_URL http://immich:2283 Immich server URL
IMPORT_DELAY 30 Seconds between folder imports
IMPORT_DELETE_ON_SUCCESS true Delete source after import

Get your API key from Immich: Account Settings β†’ API Keys β†’ New API Key

πŸ“ Import Watch Folders (Multi-User)

The Import Watch service monitors folders and automatically imports photos/videos to the correct user's Immich library.

Setup:

  1. Get API keys for each user from Immich: Account Settings β†’ API Keys β†’ New API Key

  2. Add users to your .env file:

    ./scripts/add-import-user.sh rusty "your-api-key-here"
    ./scripts/add-import-user.sh lauren "laurens-api-key"

    Or manually add to .env:

    IMPORT_USER_RUSTY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    IMPORT_USER_LAUREN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  3. Build and start:

    docker compose up -d --build import_watch
  4. Share the import folder on your network (optional):

    • The default location is /srv/immich/import
    • Share via Samba for Windows access

Folder structure:

/srv/immich/import/
β”œβ”€β”€ rusty/      # Drop files here β†’ imports to Rusty's library
β”œβ”€β”€ lauren/     # Drop files here β†’ imports to Lauren's library
β”œβ”€β”€ miller/
β”œβ”€β”€ hunter/
└── harper/

Optional settings in .env:

Variable Default Description
IMPORT_WATCH_DIR /srv/immich/import Watch folder location
IMPORT_SCAN_INTERVAL 30 Seconds between scans
IMPORT_DELETE_AFTER true Delete files after import
☁️ Proton Drive Backup (Multi-User)

Backup each user's photos to their own Proton Drive account automatically.

Prerequisites:

  1. Install rclone:

    curl https://rclone.org/install.sh | sudo bash
    apt install jq  # For user ID lookup
  2. Configure a Proton Drive remote for each user:

    rclone config
    # Name: proton_rusty  (lowercase, matches PROTON_USER_RUSTY)
    # Storage: protondrive
    # Follow prompts to authenticate with Rusty's Proton account
    
    # Repeat for each user:
    # proton_lauren, proton_miller, etc.
  3. Get Immich user IDs:

    • Go to Immich β†’ Administration β†’ Users
    • Click on a user
    • Copy the UUID from the URL: /admin/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  4. Add to your .env:

    # Proton emails (determines rclone remote name)
    PROTON_USER_RUSTY=rusty@proton.me
    PROTON_USER_LAUREN=lauren@protonmail.com
    
    # Immich user IDs (maps username to library folder)
    PROTON_IMMICH_ID_RUSTY=abc12345-6789-...
    PROTON_IMMICH_ID_LAUREN=def98765-4321-...
  5. Test with a dry run:

    ./scripts/backup-to-proton.sh --dry-run
  6. Schedule daily backups (add to crontab):

    crontab -e
    # Add this line:
    0 3 * * * /path/to/homelab/scripts/backup-to-proton.sh

How it works:

/srv/immich/library/
β”œβ”€β”€ abc12345-.../     β†’ proton_rusty:Photos/Immich/
β”œβ”€β”€ def98765-.../     β†’ proton_lauren:Photos/Immich/
└── ...

Each user's Immich library syncs to their personal Proton Drive with incremental updates.

Optional settings in .env:

Variable Default Description
IMMICH_LIBRARY /srv/immich/library Immich library location
PROTON_DEST_FOLDER Photos/Immich Folder in Proton Drive
PROTON_LOG_FILE /var/log/proton-backup.log Log file location

πŸ› οΈ Commands

Command Description
docker compose logs -f View live logs
docker compose restart Restart all services
./scripts/update.sh Pull latest images & restart
./scripts/verify.sh Check stack health
./scripts/import-photos.sh Bulk import photos (requires API key in .env)
./scripts/backup-to-proton.sh Backup photos to Proton Drive
./scripts/reset.sh Reset to fresh state
docker compose down Stop everything

πŸ”§ Troubleshooting

Tunnel not connecting

Verify your TUNNEL_TOKEN in .env matches the token from Cloudflare Zero Trust β†’ Tunnels.

HTTPS certificate errors

Verify your CF_API_TOKEN has DNS edit permissions for your zone.

NFS mount fails or system hangs
  • Verify NAS IP and share path with showmount -e <NAS_IP>
  • Check NAS permissions allow your server's IP
  • Ensure nfs-common is installed: sudo apt install nfs-common
  • Use soft mount option to prevent hangs: add soft,timeo=30,retrans=3 to fstab
Photos not appearing

Verify UPLOAD_LOCATION exists and has correct permissions:

ls -la /mnt/photos
Need to start over?
./scripts/reset.sh

This removes all config and lets you re-run the installer fresh.


πŸ“„ License

MIT Β© Rusty Green

About

Docker-based self hosted photos (immich) server

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages