Initial commit: Immich Docker setup
- Add docker-compose.yml with Immich v2 configuration - Include all required services (server, ML, postgres, redis) - Add .env.example with secure defaults - Create comprehensive README with setup instructions - Add backup and health check scripts - Configure .gitignore for data directories Deployed and tested on Netcup VPS at photos.jeffemmett.com Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
f1999a7813
|
|
@ -0,0 +1,22 @@
|
|||
# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables
|
||||
|
||||
# The location where your uploaded files are stored
|
||||
UPLOAD_LOCATION=./library
|
||||
|
||||
# The location where your database files are stored. Network shares are not supported for the database
|
||||
DB_DATA_LOCATION=./postgres
|
||||
|
||||
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||
TZ=Etc/UTC
|
||||
|
||||
# The Immich version to use. You can pin this to a specific version like "v2.1.0"
|
||||
IMMICH_VERSION=v2
|
||||
|
||||
# Connection secret for postgres. You should change it to a random password
|
||||
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||
DB_PASSWORD=CHANGE_ME_TO_RANDOM_PASSWORD
|
||||
|
||||
# The values below this line do not need to be changed
|
||||
###################################################################################
|
||||
DB_USERNAME=postgres
|
||||
DB_DATABASE_NAME=immich
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Environment files
|
||||
.env
|
||||
|
||||
# Data directories
|
||||
library/
|
||||
postgres/
|
||||
backups/
|
||||
upload/
|
||||
uploads/
|
||||
thumbs/
|
||||
profile/
|
||||
video/
|
||||
encoded-video/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
# Immich Docker Setup
|
||||
|
||||
Self-hosted photo and video management solution with Docker Compose.
|
||||
|
||||
## Overview
|
||||
|
||||
This repository contains a production-ready Docker Compose setup for [Immich](https://immich.app/), a high-performance self-hosted photo and video management solution.
|
||||
|
||||
## Features
|
||||
|
||||
- 🐳 Easy Docker Compose deployment
|
||||
- 🔒 Secure configuration with environment variables
|
||||
- 📦 Automated daily database backups
|
||||
- 🔄 Health checks for all services
|
||||
- 🚀 Reverse proxy ready (Caddy/Nginx)
|
||||
- 📱 Mobile app support (iOS/Android)
|
||||
|
||||
## Architecture
|
||||
|
||||
This setup includes the following services:
|
||||
|
||||
- **immich-server**: Main application server
|
||||
- **immich-machine-learning**: AI-powered photo recognition and classification
|
||||
- **postgres**: Database with vector extensions for AI features
|
||||
- **redis/valkey**: Caching layer
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker 20.10+ and Docker Compose v2.0+
|
||||
- At least 4GB RAM
|
||||
- 10GB+ storage (plus space for your media)
|
||||
|
||||
### Installation
|
||||
|
||||
1. Clone this repository:
|
||||
```bash
|
||||
git clone https://gitea.jeffemmett.com/jeff/immich-docker.git
|
||||
cd immich-docker
|
||||
```
|
||||
|
||||
2. Create your environment file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
3. Generate a secure database password:
|
||||
```bash
|
||||
# On Linux/macOS
|
||||
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||
|
||||
# Or use this one-liner to update .env directly
|
||||
sed -i "s/CHANGE_ME_TO_RANDOM_PASSWORD/$(openssl rand -base64 32 | tr -d '=+\/' | cut -c1-32)/" .env
|
||||
```
|
||||
|
||||
4. (Optional) Customize settings in `.env`:
|
||||
- `UPLOAD_LOCATION`: Where your photos/videos are stored
|
||||
- `DB_DATA_LOCATION`: Database storage location
|
||||
- `TZ`: Your timezone
|
||||
- `IMMICH_VERSION`: Pin to specific version if desired
|
||||
|
||||
5. Start the services:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
6. Access Immich at `http://localhost:2283`
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All configuration is done through the `.env` file. See `.env.example` for all available options.
|
||||
|
||||
Key variables:
|
||||
- `UPLOAD_LOCATION`: Media storage path (default: `./library`)
|
||||
- `DB_DATA_LOCATION`: Database path (default: `./postgres`)
|
||||
- `DB_PASSWORD`: PostgreSQL password (**must change before first run**)
|
||||
- `IMMICH_VERSION`: Version tag (default: `v2`)
|
||||
- `TZ`: Timezone (default: `Etc/UTC`)
|
||||
|
||||
### Reverse Proxy Setup
|
||||
|
||||
#### With Caddy
|
||||
|
||||
Create a Caddyfile:
|
||||
```caddy
|
||||
photos.yourdomain.com {
|
||||
reverse_proxy localhost:2283
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
Referrer-Policy "no-referrer-when-downgrade"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### With Nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name photos.yourdomain.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:2283;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backup & Restore
|
||||
|
||||
### Automated Backups
|
||||
|
||||
Daily backups are configured via cron (if using the provided scripts):
|
||||
|
||||
```bash
|
||||
# Run backup manually
|
||||
./scripts/backup-database.sh
|
||||
|
||||
# Set up automated daily backups (2 AM)
|
||||
crontab -e
|
||||
# Add: 0 2 * * * /path/to/immich-docker/scripts/backup-database.sh >> /var/log/immich-backup.log 2>&1
|
||||
```
|
||||
|
||||
### Manual Backup
|
||||
|
||||
```bash
|
||||
# Backup database
|
||||
docker compose exec -T database pg_dump -U postgres immich | gzip > backup-$(date +%Y%m%d_%H%M%S).sql.gz
|
||||
|
||||
# Backup uploaded files
|
||||
tar -czf library-backup-$(date +%Y%m%d).tar.gz library/
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
|
||||
```bash
|
||||
# Restore database
|
||||
gunzip -c backup.sql.gz | docker compose exec -T database psql -U postgres immich
|
||||
|
||||
# Restore files
|
||||
tar -xzf library-backup.tar.gz
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Update Immich
|
||||
|
||||
```bash
|
||||
# Pull latest images
|
||||
docker compose pull
|
||||
|
||||
# Restart with new images
|
||||
docker compose up -d
|
||||
|
||||
# Remove old images
|
||||
docker image prune -f
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose logs -f immich-server
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
# Check container status
|
||||
docker compose ps
|
||||
|
||||
# Run health check script
|
||||
./scripts/health-check.sh
|
||||
```
|
||||
|
||||
## Mobile Apps
|
||||
|
||||
Download the Immich mobile app to automatically backup your photos:
|
||||
|
||||
- **iOS**: [App Store - Immich](https://apps.apple.com/app/immich/id1613945652)
|
||||
- **Android**: [Play Store - Immich](https://play.google.com/store/apps/details?id=app.alextran.immich)
|
||||
|
||||
Configure the app with:
|
||||
- Server URL: `https://photos.yourdomain.com` (or your server address)
|
||||
- Email/Password: Created during first-time setup
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Cannot connect to Immich
|
||||
|
||||
1. Check if containers are running:
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
2. Check logs for errors:
|
||||
```bash
|
||||
docker compose logs immich-server
|
||||
```
|
||||
|
||||
3. Verify port is accessible:
|
||||
```bash
|
||||
curl http://localhost:2283
|
||||
```
|
||||
|
||||
### Database connection errors
|
||||
|
||||
1. Check database is healthy:
|
||||
```bash
|
||||
docker compose exec database pg_isready -U postgres
|
||||
```
|
||||
|
||||
2. Verify DB_PASSWORD matches in `.env`
|
||||
|
||||
### Out of disk space
|
||||
|
||||
1. Check Docker disk usage:
|
||||
```bash
|
||||
docker system df
|
||||
```
|
||||
|
||||
2. Clean up old images and containers:
|
||||
```bash
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
## Hardware Acceleration
|
||||
|
||||
For better performance with video transcoding and ML inference, enable hardware acceleration:
|
||||
|
||||
1. Uncomment the appropriate `extends` section in `docker-compose.yml`
|
||||
2. Download the appropriate hwaccel file from Immich docs
|
||||
3. Restart services
|
||||
|
||||
See [Immich Hardware Acceleration Docs](https://docs.immich.app/features/ml-hardware-acceleration) for details.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- ✅ Change `DB_PASSWORD` to a strong random password
|
||||
- ✅ Use HTTPS with a reverse proxy
|
||||
- ✅ Keep Immich updated regularly
|
||||
- ✅ Restrict port 2283 to localhost if using a reverse proxy
|
||||
- ✅ Enable firewall on your server
|
||||
- ✅ Regular backups of database and media files
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Official Immich Docs](https://docs.immich.app/)
|
||||
- [Docker Installation Guide](https://docs.immich.app/install/docker-compose)
|
||||
- [Environment Variables Reference](https://docs.immich.app/install/environment-variables)
|
||||
|
||||
## License
|
||||
|
||||
This setup configuration is MIT licensed. Immich itself is AGPL-3.0 licensed.
|
||||
|
||||
## Support
|
||||
|
||||
- [Immich GitHub](https://github.com/immich-app/immich)
|
||||
- [Immich Discord](https://discord.gg/immich)
|
||||
- [Immich Documentation](https://docs.immich.app/)
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#
|
||||
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||
#
|
||||
# Make sure to use the docker-compose.yml of the current release:
|
||||
#
|
||||
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
#
|
||||
# The compose file on main may not be compatible with the latest release.
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||
# extends:
|
||||
# file: hwaccel.transcoding.yml
|
||||
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
|
||||
volumes:
|
||||
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
|
||||
- ${UPLOAD_LOCATION}:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- '2283:2283'
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
|
||||
# file: hwaccel.ml.yml
|
||||
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||
volumes:
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
|
||||
# DB_STORAGE_TYPE: 'HDD'
|
||||
volumes:
|
||||
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||
shm_size: 128mb
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
model-cache:
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
# Immich Database Backup Script
|
||||
|
||||
BACKUP_DIR=~/immich/backups
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="$BACKUP_DIR/immich-db-$TIMESTAMP.sql.gz"
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo "Starting database backup: $(date)"
|
||||
|
||||
# Create backup
|
||||
cd ~/immich
|
||||
docker-compose exec -T postgres pg_dumpall -U postgres | gzip > "$BACKUP_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ Backup successful: $BACKUP_FILE"
|
||||
|
||||
# Get backup size
|
||||
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo " Size: $SIZE"
|
||||
|
||||
# Keep only last 30 days of backups
|
||||
find "$BACKUP_DIR" -name "immich-db-*.sql.gz" -mtime +30 -delete
|
||||
|
||||
# Count remaining backups
|
||||
COUNT=$(ls -1 "$BACKUP_DIR"/immich-db-*.sql.gz 2>/dev/null | wc -l)
|
||||
echo " Total backups: $COUNT"
|
||||
else
|
||||
echo "✗ Backup failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Backup completed: $(date)"
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#!/bin/bash
|
||||
# Immich Health Check Script
|
||||
|
||||
echo "========================================="
|
||||
echo "Immich Health Check - $(date)"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Check Docker containers
|
||||
echo "📦 Docker Containers:"
|
||||
cd ~/immich
|
||||
CONTAINERS=$(docker-compose ps --services)
|
||||
ALL_UP=true
|
||||
|
||||
for service in $CONTAINERS; do
|
||||
STATUS=$(docker-compose ps -q $service | xargs docker inspect -f '{{.State.Status}}')
|
||||
if [ "$STATUS" = "running" ]; then
|
||||
echo " ✓ $service: running"
|
||||
else
|
||||
echo " ✗ $service: $STATUS"
|
||||
ALL_UP=false
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Check Immich API
|
||||
echo "🌐 Immich API:"
|
||||
API_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:2283/api/server/ping 2>/dev/null)
|
||||
if [ "$API_RESPONSE" = "200" ]; then
|
||||
echo " ✓ API responding (HTTP $API_RESPONSE)"
|
||||
else
|
||||
echo " ✗ API not responding (HTTP $API_RESPONSE)"
|
||||
ALL_UP=false
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check disk space
|
||||
echo "💾 Storage Usage:"
|
||||
USAGE=$(df -h ~/immich | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||
AVAILABLE=$(df -h ~/immich | tail -1 | awk '{print $4}')
|
||||
echo " Disk usage: ${USAGE}%"
|
||||
echo " Available: $AVAILABLE"
|
||||
|
||||
if [ "$USAGE" -gt 85 ]; then
|
||||
echo " ⚠️ Warning: Disk usage above 85%"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check library size
|
||||
echo "📸 Library Statistics:"
|
||||
UPLOAD_SIZE=$(du -sh ~/immich/library 2>/dev/null | cut -f1)
|
||||
echo " Library size: $UPLOAD_SIZE"
|
||||
|
||||
PHOTO_COUNT=$(find ~/immich/library -type f 2>/dev/null | wc -l)
|
||||
echo " File count: $PHOTO_COUNT"
|
||||
echo ""
|
||||
|
||||
# Check latest backup
|
||||
echo "💾 Latest Backup:"
|
||||
LATEST_BACKUP=$(ls -t ~/immich/backups/immich-db-*.sql.gz 2>/dev/null | head -1)
|
||||
if [ -n "$LATEST_BACKUP" ]; then
|
||||
BACKUP_DATE=$(stat -c %y "$LATEST_BACKUP" | cut -d' ' -f1)
|
||||
BACKUP_SIZE=$(du -h "$LATEST_BACKUP" | cut -f1)
|
||||
echo " ✓ Latest backup: $(basename $LATEST_BACKUP)"
|
||||
echo " Date: $BACKUP_DATE"
|
||||
echo " Size: $BACKUP_SIZE"
|
||||
|
||||
# Check backup age
|
||||
BACKUP_AGE=$((($(date +%s) - $(stat -c %Y "$LATEST_BACKUP")) / 86400))
|
||||
if [ "$BACKUP_AGE" -gt 1 ]; then
|
||||
echo " ⚠️ Warning: Backup is $BACKUP_AGE days old"
|
||||
fi
|
||||
else
|
||||
echo " ✗ No backups found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Overall status
|
||||
echo "========================================="
|
||||
if [ "$ALL_UP" = true ]; then
|
||||
echo "Status: ✓ All systems operational"
|
||||
else
|
||||
echo "Status: ✗ Issues detected - check above"
|
||||
fi
|
||||
echo "========================================="
|
||||
Loading…
Reference in New Issue