ChronoFrame is a self-hosted album maintained by a Chinese developer, aimed at photography portfolios rather than backups: masonry layout, WebGL zoom, Live Photo, map footprints and password-protected albums in a clean interface. One container, one command, and it is live.
1. Why it is worth trying
- Built with Nuxt 4 + Vue, frontend and backend in one, extremely simple to deploy.
- Live Photo support (matching JPG/HEIC and MOV files paired automatically), EXIF and reverse geocoding.
- Albums can be hidden or password-protected, so you can publish only part of your library.
- Storage can be local, S3-compatible object storage or OpenList, switchable in the admin UI.
- AGPL-3.0, open code, featured by HelloGitHub.
2. Image and ports
| Item | Value |
|---|---|
| Image (GHCR) | ghcr.io/hoshinosuzumi/chronoframe:latest |
| Image (Docker Hub) | hoshinosuzumi/chronoframe:latest |
| Web port | 3000 |
| Data directory | /app/data (database, settings, local photos) |
3. Deploy with Compose
mkdir -p /opt/chronoframe && cd /opt/chronoframe
Create docker-compose.yml:
services:
chronoframe:
image: ghcr.io/hoshinosuzumi/chronoframe:latest
container_name: chronoframe
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./data:/app/data
environment:
TZ: "Asia/Shanghai"
docker compose up -d
docker compose logs -f chronoframe
Open http://SERVER_IP:3000 and the setup wizard appears.
4. Setup wizard
- Create the administrator account (email and password).
- Enter the site name and address (for example
https://photos.example.com). - Choose storage: for local storage use
/app/data/storage; for S3 or OpenList fill in the endpoint, bucket and keys. - The map feature needs a MapLibre or Mapbox token; you can skip it and add it later in the admin UI.
After finishing, sign in and upload photos at /dashboard. A job queue processes EXIF, thumbnails, locations and Live Photo pairs; failed jobs can be retried.


5. Reverse proxy and HTTPS
server {
listen 443 ssl http2;
server_name photos.example.com;
ssl_certificate /etc/letsencrypt/live/photos.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/photos.example.com/privkey.pem;
client_max_body_size 2000M;
proxy_read_timeout 600s;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
The official docs require HTTPS for public deployments; otherwise login cookies and uploads break.
6. Backup and upgrades
- Backup: the whole
./datadirectory is everything (SQLite, settings, local photos). With S3, back up./dataplus the bucket. - Upgrade:
docker compose pull && docker compose up -d. Pin a tag such asv1.0.0instead oflatestif you want stability. - Migration: copy
./datato the new host and start the same Compose file.
7. Troubleshooting
Uploads are queued forever? Check docker compose logs -f chronoframe; usually a storage path or S3 credential problem.
Thumbnails missing? Make sure the storage directory is writable by the container and that ffmpeg jobs are not failing on unusual formats.
Can it back up my phone automatically? No. ChronoFrame is a portfolio front end; pair it with Immich or a sync tool if you need automatic backup.
8. Wrap-up
ChronoFrame is the lightest way to get a beautiful photo site online. It will not replace a full backup system, but for a personal portfolio or a yearly family album it is hard to beat on effort versus result.
Related: Immich tutorial · 2026 self-hosted album comparison


