ImageSilo is a Docker-first image host built as one Go process with SQLite and local file storage. It provides authenticated uploads, stable public URLs, historical aliases, scoped API tokens, image processing, and a responsive React administration interface without Redis, an external database, or a background job service.
> Release images use immutable version tags and the mutable `latest` tag. Production deployments should pin an immutable version tag or digest.
## Screenshots
## Features
- Store JPEG, PNG, WebP, and GIF images with strict decoding and a 16 MP safety limit
- Preserve original bytes or explicitly compress and convert static images to WebP
- Deliver public images, historical aliases, and path-preserving migration files with Range, conditional request, and HEAD support
- Keep the delivery hot path in memory, with no SQLite lookup per image request
- Manage public/private visibility, administrator sessions, CSRF protection, login rate limits, and scoped API tokens
- Upload, search, filter, inspect, batch-update, and permanently delete images from the built-in web interface
- Import legacy URLs, verify bytes by SHA-256, inspect storage, and rebuild in-memory indexes
- Run as a non-root, multi-architecture container with image processing concurrency set to `1` by default
## Quick Start
Docker is the supported production runtime. The following starts a local evaluation instance with a named volume:
```bash
export IMAGESILO_IMAGE=ghcr.io/willxup/imagesilo:latest
docker pull "$IMAGESILO_IMAGE"
docker volume create imagesilo-data
docker run --detach \
--name imagesilo \
--restart unless-stopped \
--publish 127.0.0.1:8080:8080 \
--env IMAGESILO_COOKIE_SECURE=false \
--env IMAGESILO_PROCESSING_CONCURRENCY=1 \
--volume imagesilo-data:/data \
"$IMAGESILO_IMAGE"
```
On the first launch, run `docker logs -f imagesilo` and copy the `bootstrap_token` printed by ImageSilo. Open `http://127.0.0.1:8080/admin/setup`, enter that token, and create the administrator account. The token exists only in memory, is replaced after an uninitialized restart, and is erased after setup succeeds. The `imagesilo admin create` command remains available for scripted deployments.
For production, terminate HTTPS at a reverse proxy, keep `IMAGESILO_COOKIE_SECURE=true`, and back up the complete `/data` directory while writes are stopped. See the [deployment guide](./docs/deployment.md) before exposing the service.
To preserve an existing image URL tree without creating one alias at a time, mount it read-only at `/data/migrations`. A file mounted as `/data/migrations/i/2022/04/example.jpg` is immediately available at `/i/2022/04/example.jpg`. Managed historical aliases take precedence when both sources contain the same path; only JPEG, PNG, WebP, and GIF files are exposed. The Migration management page can browse and copy these URLs in read-only mode; its file list uses a 30-minute lazy snapshot with manual refresh, while direct image delivery is never gated by that list cache. Only for a dedicated migration copy, make the mount writable and set `IMAGESILO_MIGRATION_MUTATIONS=true` to enable permanent deletion.
## Enable Migration Deletion
Migration deletion is disabled by default and should only be enabled for a dedicated migration copy with an independent backup. The environment variable, mount mode, and host permissions must all allow deletion. Setting the variable while retaining a read-only mount is not sufficient.
Enable the capability in `docker-compose.yaml` and mount `/data/migrations` writable:
```yaml
services:
imagesilo:
image: ghcr.io/willxup/imagesilo:latest
environment:
IMAGESILO_MIGRATION_MUTATIONS: "true"
volumes:
- ./data:/data
- ./migration:/data/migrations:rw
```
If the existing migration mount ends in `:ro`, change it to `:rw` or omit the mount mode. A separate migration mount is unnecessary when the files already live inside a writable `./data/migrations` directory.
The container runs as UID/GID `10001:10001`. Apply these permissions only to a dedicated migration copy:
```bash
sudo chown -R 10001:10001 ./migration
sudo find ./migration -type d -exec chmod 750 {} +
sudo find ./migration -type f -exec chmod 640 {} +
```
Validate the configuration and recreate the container. A regular `docker compose restart` does not apply new environment variables:
```bash
docker compose config --quiet
docker compose up -d --force-recreate
docker compose exec imagesilo sh -c \
'test "$IMAGESILO_MIGRATION_MUTATIONS" = true && test -w /data/migrations'
```
Deletion cannot be undone. After deleting an image, ImageSilo recursively removes truly empty parent directories, including empty top-level `i` or `images` directories, but never removes the `/data/migrations` root. Do not enable this capability for the only copy of a legacy image library.
## Design Boundaries
| Area | Implementation |
| --- | --- |
| Runtime | One Go process in one container |
| Metadata | SQLite in `/data/db` |
| Images | Local files in `/data/images` |
| Path-preserving migrations | Image tree in `/data/migrations`, read-only by default with opt-in deletion |
| Cache | Local thumbnails in `/data/cache` |
| Processing | libvips, concurrency `1` by default |
| Delivery | File streaming with ETag revalidation; unlimited by default (`0`), optionally capped |
| Platforms | `linux/amd64`, `linux/arm64` |
| Production storage | Docker named volume or local bind mount |
NFS and SMB are not supported. Do not run two ImageSilo containers against the same writable `/data` directory.
## Local Development
### Prerequisites
- Go >=1.26.10
- Node.js >=24.22.1
- npm
Local toolchains need only meet these minimum versions; no exact version is pinned. npm enforces the Node.js minimum during installation. CI uses the latest Go 1.26.x and Node.js 24.x; Docker builds use rolling Debian Bookworm image tags with `--pull`.
### Build and Verify
```bash
npm --prefix web ci
make check
make build
```
Create an administrator and run the local binary:
```bash
IMAGESILO_DATA_DIR="$PWD/data" \
./bin/imagesilo admin create --email admin@example.com
IMAGESILO_DATA_DIR="$PWD/data" \
IMAGESILO_COOKIE_SECURE=false \
./bin/imagesilo serve
```
Use `/healthz` for liveness and `/readyz` for SQLite readiness.
UI and browser behavior changes are checked on demand in an existing browser, manually or with computer-use tools. No project-managed browser or browser-test framework is required. CI runs `make check` (build, lint, type checks, and unit/integration tests), not browser acceptance. Follow the [browser acceptance checklist](./docs/browser-acceptance.md), record actual results, and explicitly mark unverified scenarios.
## Documentation
| Topic | Guide |
| --- | --- |
| Docker deployment, backup, upgrade, and rollback | [Deployment](./docs/deployment.md) |
| API tokens, curl, PicGo, and ShareX | [API token usage](./docs/api-token-usage.md) |
| Legacy image and URL migration | [Imports](./docs/imports.md) |
| Inspection, cleanup, and index rebuilds | [Operations](./docs/operations.md) |
| Architecture and data model | [Architecture](./docs/architecture.md) · [Data model](./docs/data-model.md) |
| Security and performance | [Security audit](./docs/security-audit.md) · [Performance baseline](./docs/performance-baseline.md) |
| Release process and verification | [Release guide](./docs/release.md) |
| HTTP API contract | [OpenAPI](./api/openapi.yaml) |
| Bundled third-party licenses | [Third-party notices](./THIRD_PARTY_NOTICES.md) |
## License
This project is open source under the [MIT License](./LICENSE).