Self-hosted personal gallery application with online photo management and albums, supporting Live/Motion Photos, EXIF parsing, geolocation recognition, and an explore map.
See the code
Languages: English | 中文
A smooth photo display and management application, supporting multiple image formats and large-size image rendering.
We recommend deploying with the prebuilt Docker image. View the image on ghcr
Create a .env file and configure environment variables.
Below is a minimal configuration example. For complete configuration options, see Configuration Guide:
# Admin email (required)
CFRAME_ADMIN_EMAIL=
# Admin username (optional, default Chronoframe)
CFRAME_ADMIN_NAME=
# Admin password (optional, default CF1234@!)
CFRAME_ADMIN_PASSWORD=
# Site metadata (all optional)
NUXT_PUBLIC_APP_TITLE=
NUXT_PUBLIC_APP_SLOGAN=
NUXT_PUBLIC_APP_AUTHOR=
# External avatar URL (optional; you can also upload an avatar from the dashboard, stored in the configured storage provider)
NUXT_PUBLIC_APP_AVATAR_URL=
# Map provider (maplibre/mapbox)
NUXT_PUBLIC_MAP_PROVIDER=maplibre
# MapTiler access token for MapLibre
NUXT_PUBLIC_MAP_MAPLIBRE_TOKEN=
# Mapbox access token for Mapbox
NUXT_PUBLIC_MAPBOX_ACCESS_TOKEN=
# Mapbox unrestricted token (optional, reverse geocoding)
NUXT_MAPBOX_ACCESS_TOKEN=
# Storage provider (local, s3 or openlist)
NUXT_STORAGE_PROVIDER=local
NUXT_PROVIDER_LOCAL_PATH=/app/data/storage
# Session password (32‑char random string, required)
NUXT_SESSION_PASSWORD=
# Secret key for stable signing og images
# Use: npx nuxt-og-image generate-secret
NUXT_OG_IMAGE_SECRET=
Use the published image on GitHub Container Registry and Docker Hub. Choose the source that works best for your network:
docker pull ghcr.io/hoshinosuzumi/chronoframe:latest
docker pull hoshinosuzumi/chronoframe:latest
Run with customized environment variables:
docker run -d --name chronoframe -p 3000:3000 -v $(pwd)/data:/app/data --env-file .env ghcr.io/hoshinosuzumi/chronoframe:latest
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
env_file:
- .env
Start:
docker compose up -d
If
CFRAME_ADMIN_EMAILandCFRAME_ADMIN_PASSWORDare not set, the default admin account is:
- Email:
[email protected]- Password:
CF1234@!

# With pnpm (recommended)
pnpm install
# Or with other package managers
npm install
yarn install
cp .env.example .env
# 2. Generate migration files (optional)
pnpm db:generate
# 3. Run database migrations
pnpm db:migrate
pnpm dev
App will start at http://localhost:3000.
chronoframe/
├── app/ # Nuxt app
│ ├── components/ # Components
│ ├── pages/ # Page routes
│ ├── composables/ # Composables
│ └── stores/ # Pinia stores
├── packages/
│ └── webgl-image/ # WebGL image viewer
├── server/
│ ├── api/ # API routes
│ ├── database/ # DB schema & migrations
│ └── services/ # Business logic services
└── shared/ # Shared types & utils
# Development (with dependencies build)
pnpm dev
# Build only dependencies
pnpm build:deps
# Production build
pnpm build
# Database operations
pnpm db:generate # Generate migration files
pnpm db:migrate # Run migrations
# Preview production build
pnpm preview
Contributions are welcome! Please:
This project is licensed under the MIT License.
Timothy Yin
On first startup, an admin user is created based on CFRAME_ADMIN_EMAIL, CFRAME_ADMIN_NAME, and CFRAME_ADMIN_PASSWORD. The email must match your GitHub account email used for login.
Supported formats: JPEG, PNG, HEIC/HEIF, MOV (for Live Photos).
Currently only S3-compatible storage is supported. GitHub and local storage support is planned.
The map is used to browse photo locations and render mini-maps in photo details. Currently Mapbox is used. After registering, get an access token and set it to the MAPBOX_TOKEN variable.
Ensure the image (.heic) and video (.mov) share the same filename (e.g., IMG_1234.heic and IMG_1234.mov). Upload order does not matter. If not recognized, you can trigger pairing manually from the dashboard.
Direct import of existing photos is not yet supported. A directory scanning import feature is planned.
This project was inspired by Afilmory, another excellent personal gallery project.
Thanks to the following open-source projects and libraries:
3,496 followers · starred Oct 2025
352 followers · starred Sep 2025
565 followers · starred May 2026
112 followers · starred Jun 2026
Self-hosted personal gallery application with online photo management and albums, supporting Live/Motion Photos, EXIF parsing, geolocation recognition, and an explore map.
See the code
Languages: English | 中文
A smooth photo display and management application, supporting multiple image formats and large-size image rendering.
We recommend deploying with the prebuilt Docker image. View the image on ghcr
Create a .env file and configure environment variables.
Below is a minimal configuration example. For complete configuration options, see Configuration Guide:
# Admin email (required)
CFRAME_ADMIN_EMAIL=
# Admin username (optional, default Chronoframe)
CFRAME_ADMIN_NAME=
# Admin password (optional, default CF1234@!)
CFRAME_ADMIN_PASSWORD=
# Site metadata (all optional)
NUXT_PUBLIC_APP_TITLE=
NUXT_PUBLIC_APP_SLOGAN=
NUXT_PUBLIC_APP_AUTHOR=
# External avatar URL (optional; you can also upload an avatar from the dashboard, stored in the configured storage provider)
NUXT_PUBLIC_APP_AVATAR_URL=
# Map provider (maplibre/mapbox)
NUXT_PUBLIC_MAP_PROVIDER=maplibre
# MapTiler access token for MapLibre
NUXT_PUBLIC_MAP_MAPLIBRE_TOKEN=
# Mapbox access token for Mapbox
NUXT_PUBLIC_MAPBOX_ACCESS_TOKEN=
# Mapbox unrestricted token (optional, reverse geocoding)
NUXT_MAPBOX_ACCESS_TOKEN=
# Storage provider (local, s3 or openlist)
NUXT_STORAGE_PROVIDER=local
NUXT_PROVIDER_LOCAL_PATH=/app/data/storage
# Session password (32‑char random string, required)
NUXT_SESSION_PASSWORD=
# Secret key for stable signing og images
# Use: npx nuxt-og-image generate-secret
NUXT_OG_IMAGE_SECRET=
Use the published image on GitHub Container Registry and Docker Hub. Choose the source that works best for your network:
docker pull ghcr.io/hoshinosuzumi/chronoframe:latest
docker pull hoshinosuzumi/chronoframe:latest
Run with customized environment variables:
docker run -d --name chronoframe -p 3000:3000 -v $(pwd)/data:/app/data --env-file .env ghcr.io/hoshinosuzumi/chronoframe:latest
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
env_file:
- .env
Start:
docker compose up -d
If
CFRAME_ADMIN_EMAILandCFRAME_ADMIN_PASSWORDare not set, the default admin account is:
- Email:
[email protected]- Password:
CF1234@!

# With pnpm (recommended)
pnpm install
# Or with other package managers
npm install
yarn install
cp .env.example .env
# 2. Generate migration files (optional)
pnpm db:generate
# 3. Run database migrations
pnpm db:migrate
pnpm dev
App will start at http://localhost:3000.
chronoframe/
├── app/ # Nuxt app
│ ├── components/ # Components
│ ├── pages/ # Page routes
│ ├── composables/ # Composables
│ └── stores/ # Pinia stores
├── packages/
│ └── webgl-image/ # WebGL image viewer
├── server/
│ ├── api/ # API routes
│ ├── database/ # DB schema & migrations
│ └── services/ # Business logic services
└── shared/ # Shared types & utils
# Development (with dependencies build)
pnpm dev
# Build only dependencies
pnpm build:deps
# Production build
pnpm build
# Database operations
pnpm db:generate # Generate migration files
pnpm db:migrate # Run migrations
# Preview production build
pnpm preview
Contributions are welcome! Please:
This project is licensed under the MIT License.
Timothy Yin
On first startup, an admin user is created based on CFRAME_ADMIN_EMAIL, CFRAME_ADMIN_NAME, and CFRAME_ADMIN_PASSWORD. The email must match your GitHub account email used for login.
Supported formats: JPEG, PNG, HEIC/HEIF, MOV (for Live Photos).
Currently only S3-compatible storage is supported. GitHub and local storage support is planned.
The map is used to browse photo locations and render mini-maps in photo details. Currently Mapbox is used. After registering, get an access token and set it to the MAPBOX_TOKEN variable.
Ensure the image (.heic) and video (.mov) share the same filename (e.g., IMG_1234.heic and IMG_1234.mov). Upload order does not matter. If not recognized, you can trigger pairing manually from the dashboard.
Direct import of existing photos is not yet supported. A directory scanning import feature is planned.
This project was inspired by Afilmory, another excellent personal gallery project.
Thanks to the following open-source projects and libraries:
3,496 followers · starred Oct 2025
352 followers · starred Sep 2025
565 followers · starred May 2026
112 followers · starred Jun 2026