{note} Critical Breaking Changes: Version 7 introduces significant architectural changes to the Docker setup. You must update your docker-compose configuration when upgrading from v6 to v7. Simply changing the image tag will not work.
Version 7 marks a fundamental shift in Lychee's Docker architecture:
Do note that this change also has consequences in the way Lychee reads your .env
file. Updating values in the .env file will now require a container restart to take effect.
Version 7 uses FrankenPHP which listens on port 8000 instead of the previous 80 used by nginx. Make sure to update your port mappings accordingly in your docker-compose.yml.
In order to avoid you running into issues while booting version 7, we are blocking the startup of the container if the old volume structure is detected.
volumes:
- ./lychee/conf:/conf
- ./lychee/uploads:/uploads
- ./lychee/sym:/sym
- ./lychee/logs:/logs
- ./lychee/tmp:/lychee-tmp
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp # so that uploads are not filling up the memory of the container
- ./lychee/conf/.env:/app/.env # OPTIONAL, you can manage your .env directly as variables in docker-compose.yml
- ./lychee/storage/app:/app/storage/app # OPTIONAL: for persistent storage of app data
- ./conf/user.css:/app/public/dist/user.css # OPTIONAL
- ./conf/custom.js:/app/public/dist/custom.js # OPTIONAL
{note} Notice the key changes: uploads are now at
/app/public/uploads, logs at/app/storage/logs, tmp at/app/storage/tmp, and the.envfile.
The /sym volume has been removed as Lychee no longer uses symbolic links for storage. This was a security feature that originated from version 4, but is no longer necessary as the functionality has been removed.
Important: With multiple volumes under /app/storage, you may think you could simplify the configuration by specifying one single volume for /app/storage instead. This is incorrect. Doing so will make the app exit with the error "The /app/bootstrap/cache directory must be present and writable."
Version 7 introduces a multi-service architecture with an optional worker service for background job processing.
For basic installations without background workers:
services:
lychee_api:
image: ghcr.io/lycheeorg/lychee:edge
container_name: lychee
ports:
- "${APP_PORT:-8000}:8000"
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- .env:/app/.env:ro
environment:
- DB_CONNECTION=mysql
- DB_HOST=lychee_db
- DB_PORT=3306
- DB_DATABASE=lychee
- DB_USERNAME=lychee
- DB_PASSWORD=your_password
# ... other environment variables
depends_on:
- lychee_db
networks:
- lychee
For better performance with background job processing:
services:
lychee_api:
image: ghcr.io/lycheeorg/lychee:edge
container_name: lychee
ports:
- "${APP_PORT:-8000}:8000"
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- .env:/app/.env:ro
environment:
- DB_CONNECTION=mysql
- DB_HOST=lychee_db
- QUEUE_CONNECTION=database # CRITICAL for worker mode
# ... other environment variables
depends_on:
- lychee_db
networks:
- lychee
lychee_worker:
image: ghcr.io/lycheeorg/lychee:edge
container_name: lychee_worker
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- .env:/app/.env:ro
environment:
- LYCHEE_MODE=worker # CRITICAL: Tells container to run in worker mode
- DB_CONNECTION=mysql
- DB_HOST=lychee_db
- DB_PORT=3306
- DB_DATABASE=lychee
- DB_USERNAME=lychee
- DB_PASSWORD=your_password
- QUEUE_CONNECTION=database # CRITICAL: Must match API service
# ... other environment variables
depends_on:
- lychee_db
- lychee_api
networks:
- lychee
{note} Critical: When using worker services, you must set
QUEUE_CONNECTION=database(orredisif using Redis) in both the API and worker services. Without this, jobs will not be processed properly.
The worker service provides several advantages:
You can run multiple worker instances for parallel processing:
lychee_worker:
image: ghcr.io/lycheeorg/lychee:edge
deploy:
replicas: 3 # Run 3 worker instances
# ... rest of configuration
Or manually with different container names:
lychee_worker_1:
image: ghcr.io/lycheeorg/lychee:edge
container_name: lychee_worker_1
environment:
- LYCHEE_MODE=worker
# ... rest of configuration
lychee_worker_2:
image: ghcr.io/lycheeorg/lychee:edge
container_name: lychee_worker_2
environment:
- LYCHEE_MODE=worker
# ... rest of configuration
Follow these steps to migrate from v6 to v7:
# Backup your database
docker exec lychee_db mysqldump -u lychee -p lychee > lychee_backup.sql
# Backup your uploads and configuration
cp -r ./lychee ./lychee_backup
docker-compose down
Replace your v6 docker-compose.yml with the v7 configuration. You can find the complete example at: https://github.com/LycheeOrg/Lychee/blob/master/docker-compose.yaml
If your current directory structure doesn't match the new volume mounts, reorganize:
# The uploads directory structure should remain compatible
# Ensure your uploads are in ./lychee/uploads/
Key changes to your environment configuration:
- If using workers: Add QUEUE_CONNECTION=database or QUEUE_CONNECTION=redis
- If using workers: Add LYCHEE_MODE=worker to worker service only
- Review other environment variables for any deprecated options
docker-compose up -d
Check that all services are running:
docker-compose ps
Check logs for errors:
docker-compose logs -f lychee
You will notice that after the upgrade, thumbnails are missing. You can regenerate them by running:
docker exec -it lychee php artisan lychee:recompute-album-sizes
docker exec -it lychee php artisan lychee:recompute-album-stats
or by logging into the web interface and going to Settings ⇒ Maintenance ⇒ Album Precomputed Fields.
Container keeps restarting
.env or docker-compose.yml if that is not the case you can run echo "APP_KEY=base64:$(openssl rand -base64 32)" to create a new one.Workers not processing jobs
- Verify QUEUE_CONNECTION=database is set in both API and worker services
- Verify LYCHEE_MODE=worker is set in worker service
- Check worker logs: docker-compose logs -f lychee_worker
Upload issues
- Verify volume mounts point to the correct paths
- Check file permissions on host directories
- Ensure uploads directory: ./lychee/uploads exists and is writable
Performance issues
- Consider adding worker services for background processing
- Check FrankenPHP is running (should see FrankenPHP in logs, not nginx)
- Verify QUEUE_CONNECTION is set for async job processing
Database connection errors
- Ensure database service name matches DB_HOST value
- Verify database credentials are correct
- Check database service is healthy: docker-compose ps lychee_db
For more help, visit our GitHub Discussions or Discord server.
This guide helps you migrate an existing Lychee installation from a traditional setup (e.g., /var/www/html/Lychee) to a Docker Compose deployment.
Before starting the migration, make sure you have docker compose installed. You can follow the instructions here.
{note} Critical: Always create backups before migrating. This ensures you can restore your installation if anything goes wrong.
# Backup the database
mysqldump -u your_db_user -p your_db_name > ~/lychee_db_backup.sql
# Backup the entire Lychee directory
cp -r /var/www/html/Lychee ~/lychee_backup
# Backup your .env file specifically
cp /var/www/html/Lychee/.env ~/lychee_env_backup
Create a directory for your Docker Compose setup:
mkdir -p ~/lychee-docker
cd ~/lychee-docker
# Create directories for volume mounts
mkdir -p lychee/uploads
mkdir -p lychee/storage/app
mkdir -p lychee/logs
mkdir -p lychee/tmp
mkdir -p lychee/conf
Move your existing uploads and configuration:
# Copy uploads (this may take time depending on your photo library size)
sudo cp -r /var/www/html/Lychee/public/uploads/* ~/lychee-docker/lychee/uploads/
# Copy your .env configuration
sudo cp /var/www/html/Lychee/.env ~/lychee-docker/lychee/conf/.env
# If you have custom CSS or JavaScript
sudo cp /var/www/html/Lychee/public/dist/user.css ~/lychee-docker/conf/user.css 2>/dev/null || true
sudo cp /var/www/html/Lychee/public/dist/custom.js ~/lychee-docker/conf/custom.js 2>/dev/null || true
# Set appropriate permissions
sudo chown -R $USER:$USER ~/lychee-docker/lychee
chmod -R 755 ~/lychee-docker/lychee
Create a docker-compose.yml file in ~/lychee-docker:
services:
lychee_db:
image: mariadb:11
container_name: lychee_db
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=lychee
- MYSQL_USER=lychee
- MYSQL_PASSWORD=lychee_password
volumes:
- ./lychee/lychee_db:/var/lib/mysql
networks:
- lychee
restart: unless-stopped
lychee_api:
image: ghcr.io/lycheeorg/lychee:latest
container_name: lychee
ports:
- "8000:8000" # Change the first part XXXX:8000 this to your preferred port
env_file:
- ./lychee/conf/.env
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- ./lychee/conf/.env:/app/.env
# Optional: Uncomment if you have custom CSS/JS
# - ./conf/user.css:/app/public/dist/user.css
# - ./conf/custom.js:/app/public/dist/custom.js
environment:
- APP_KEY=base64:YOUR_APP_KEY_HERE # Generate it with `openssl rand -base64 32`
- DB_CONNECTION=mysql
- DB_HOST=lychee_db
- DB_PORT=3306
- DB_DATABASE=lychee
- DB_USERNAME=lychee
- DB_PASSWORD=lychee_password
depends_on:
- lychee_db
networks:
- lychee
restart: unless-stopped
networks:
lychee:
Edit ~/lychee-docker/lychee/conf/.env to update database connection settings:
# Update these values to match your docker-compose.yml
DB_CONNECTION=mysql
DB_HOST=lychee_db
DB_PORT=3306
DB_DATABASE=lychee
DB_USERNAME=lychee
DB_PASSWORD=lychee_password
# Set the application URL
APP_URL=http://your-domain.com:8000 # Update with your domain/IP
Start the database container and import your data:
cd ~/lychee-docker
# Start only the database service
docker-compose up -d lychee_db
# Wait for the database to be ready (about 10-20 seconds)
sleep 20
# Import your database backup
docker exec -i lychee_db mysql -u lychee -plychee_password lychee < ~/lychee_db_backup.sql
# Start all services
docker-compose up -d
# Check logs to ensure everything started correctly
docker-compose logs -f lychee
Press Ctrl+C to exit log viewing.
If you're using a reverse proxy (recommended for production), configure it to forward to the Docker container.
Nginx Example:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:8000;
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;
# For large photo uploads
client_max_body_size 100M;
}
}
Apache Example:
<VirtualHost *:80>
ServerName your-domain.com
ProxyPreserveHost On
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
# For large photo uploads
LimitRequestBody 104857600
</VirtualHost>
Enable required modules and restart:
# For Apache
sudo a2enmod proxy proxy_http
sudo systemctl restart apache2
# For Nginx
sudo systemctl restart nginx
http://your-domain.com (or http://your-domain.com:8000 if not using reverse proxy)For better performance with large photo libraries, consider adding a worker service:
lychee_worker:
image: ghcr.io/lycheeorg/lychee:latest
container_name: lychee_worker
volumes:
- ./lychee/uploads:/app/public/uploads
- ./lychee/storage/app:/app/storage/app
- ./lychee/logs:/app/storage/logs
- ./lychee/tmp:/app/storage/tmp
- ./lychee/conf/.env:/app/.env:ro
environment:
- LYCHEE_MODE=worker
- DB_CONNECTION=mysql
- DB_HOST=lychee_db
- DB_PORT=3306
- DB_DATABASE=lychee
- DB_USERNAME=lychee
- DB_PASSWORD=lychee_password
- QUEUE_CONNECTION=database
depends_on:
- lychee_db
- lychee_api
networks:
- lychee
restart: unless-stopped
Also add QUEUE_CONNECTION=database to the lychee_api service environment variables, then restart:
docker-compose up -d
After confirming everything works correctly:
# Disable the old web server from starting on boot
sudo systemctl disable apache2 # or nginx
# You can remove the old installation (keep the backup!)
# sudo rm -rf /var/www/html/Lychee # Only after thorough testing!
Cannot access Lychee
- Check if containers are running: docker ps
- Check logs: docker logs lychee
- Verify port 8000 is not blocked by firewall
- If using reverse proxy, check proxy configuration
Photos not showing
- Verify uploads were copied correctly: ls -la ~/lychee-docker/lychee/uploads/
- Check volume mount permissions
- Verify file paths in database match new structure
Database connection errors
- Confirm database container is running: docker ps lychee_db
- Verify credentials in .env match docker-compose.yml
- Check database logs: docker logs lychee_db
Permission errors
- Fix ownership: sudo chown -R 33:33 ~/lychee-docker/lychee/ (33 is www-data UID/GID but can also be 82 for alpine)
- Or make directories writable: chmod -R 777 ~/lychee-docker/lychee/ (less secure)
For additional help, visit our GitHub Discussions or Discord server.
Check that the server satisfies the requirements. In particular pay attention the PHP extensions.
You can display installed PHP extensions using phpinfo().
Assuming the following tree:
/var/
|- www/
|- html/
|- Lychee/
|- <you are here>
Rename Lychee into Lychee-v3:
mv Lychee Lychee-v3
Install Lychee files by either uploading the content of the released zip or cloning the repository:
git clone https://github.com/LycheeOrg/Lychee Lychee
Move the pictures from the version 3 to the newly created installation:
mv Lychee-v3/uploads/big/* Lychee/public/uploads/big/
mv Lychee-v3/uploads/medium/* Lychee/public/uploads/medium/
mv Lychee-v3/uploads/small/* Lychee/public/uploads/small/
mv Lychee-v3/uploads/thumb/* Lychee/public/uploads/thumb/
{note} The big difference between Lychee version 3 and Lychee version 4 is the served directory, i.e. where you webserver needs to point to.
. of Lychee.public directory inside Lychee.Make sure you have the module rewrite available and enabled: a2enmod rewrite.
Modify your /etc/apache2/apache2.conf to allow .htaccess to set up the rewrite rules:
<Directory /var/www/html/Lychee>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Modify or create example.com.conf in /etc/apache2/sites-available/ to point out the served directory:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html/Lychee/public
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Enable the site:
a2ensite `example.com.conf`
Restart apache2:
systemctl restart apache2
If you are using Nginx, an example configuration can be found here.
Make sure you have DB_OLD_LYCHEE_PREFIX set in your .env to correspond the table prefix of your old database, then run php artisan migrate.
{tip} Caught a mistake or want to contribute to the documentation? Edit this page on Github!