Docker deployment
ImageTools ships as a single container: the FastAPI backend serves the API and the pre-built Vue frontend. One image, one volume, one docker-compose up.
The image
Published images are tagged from version.json by ./build.sh (see BUILD.md):
ghcr.io/githuba42r/imagetools:1.12.0 # version-pinned (recommended)
ghcr.io/githuba42r/imagetools:latest
The image includes the built frontend — there is no separate frontend artifact to deploy. The container listens on 8081 internally; the compose file maps it to 8082 on the host to avoid clashes with common dev servers.
| Endpoint | URL (default compose) |
|---|---|
| Web UI | http://localhost:8082 |
| API | http://localhost:8082/api/v1/ |
| API docs (Swagger) | http://localhost:8082/api/v1/docs |
| Health check | http://localhost:8082/health → {"status":"healthy"} |
Quick start with docker compose
From the repo root, the bundled docker-compose.yml is all you need:
git clone https://github.com/githuba42r/ImageTools.git
cd ImageTools
# optional but recommended for anything non-local:
export SESSION_SECRET_KEY=$(openssl rand -base64 32)
export INSTANCE_URL=http://localhost:8082
docker-compose up --build -d # or: docker compose up --build -d
# app at http://localhost:8082
It builds the image locally (or swap build: for image: ghcr.io/githuba42r/imagetools:1.12.0 to pull), maps 8082:8081, persists everything under the imagetools-storage volume mounted at /app/storage (uploads, processed images, SQLite), and runs a /health healthcheck.
- Stop:
docker-compose down - Clean slate (deletes all data):
docker-compose down -v - Logs:
docker-compose logs -f
Key environment variables
These are the variables the compose file passes through; the full reference is in docs/ENV_CONFIG.md and docs/DOCKER_DEPLOYMENT.md.
| Variable | Default in compose | Why it matters |
|---|---|---|
SESSION_SECRET_KEY |
change-this-secret-key-in-production-min-32-chars (override via env) |
Encryption key for stored secrets (min 32 chars). Change it in production — e.g. openssl rand -base64 32. |
CORS_ORIGINS |
http://localhost:8082,http://127.0.0.1:8082 |
Comma-separated allowed origins. Add your public URL when accessing from other hosts, e.g. https://imagetools.example.com. |
INSTANCE_URL |
http://localhost:8082 |
Your deployment's public URL. Used for Android QR enrollment, browser-addon connection URLs and the OpenRouter OAuth callback ({INSTANCE_URL}/oauth/callback). Set this to your real URL. |
OPENROUTER_APP_URL |
http://localhost:5173 (dev default; required for AI in prod) |
Public URL for OAuth callbacks — callback is auto-derived as {OPENROUTER_APP_URL}/oauth/callback. |
OPENROUTER_APP_NAME |
ImageTools |
App name shown in OpenRouter during authorization. |
OPENROUTER_API_KEY |
empty | Optional fallback key. Leave empty to make each user enroll their own account via OAuth (recommended). |
SESSION_EXPIRY_DAYS / MAX_IMAGES_PER_SESSION / MAX_UPLOAD_SIZE_MB |
7 / 5 / 20 |
Retention and upload limits. |
DEBUG / LOG_LEVEL / SERVER_HOST / SERVER_PORT |
False / INFO / 0.0.0.0 / 8081 |
Server basics — the container port is 8081; only the compose port mapping changes what you see on the host. |
SESSION_SECRET_KEY, set INSTANCE_URL (and OPENROUTER_APP_URL) to your real HTTPS domain, narrow CORS_ORIGINS, put the container behind a reverse proxy, and don't expose port 8082 directly to the internet.
Compose environment block (as shipped)
services:
imagetools:
build:
context: .
dockerfile: Dockerfile
container_name: imagetools
ports:
- "8082:8081" # Using 8082 to avoid conflicts with existing services
volumes:
- imagetools-storage:/app/storage
environment:
- DEBUG=False
- LOG_LEVEL=INFO
- SERVER_HOST=0.0.0.0
- SERVER_PORT=8081
- SESSION_EXPIRY_DAYS=7
- MAX_IMAGES_PER_SESSION=5
- MAX_UPLOAD_SIZE_MB=20
- SESSION_SECRET_KEY=${SESSION_SECRET_KEY:-change-this-secret-key-in-production-min-32-chars}
- CORS_ORIGINS=http://localhost:8082,http://127.0.0.1:8082
- INSTANCE_URL=${INSTANCE_URL:-http://localhost:8082}
- OPENROUTER_APP_NAME=ImageTools
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8081/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
imagetools-storage:
driver: local
Or plain docker run
docker run -d --name imagetools \
-p 8082:8081 \
-v imagetools-storage:/app/storage \
-e SESSION_SECRET_KEY="$(openssl rand -base64 32)" \
-e INSTANCE_URL=http://localhost:8082 \
ghcr.io/githuba42r/imagetools:1.12.0
Reverse proxy, Basic Auth & Authelia
For anything internet-facing, run the container on loopback and front it with nginx. The repo ships a complete option in deployment/ — nginx reverse proxy with HTTP Basic Authentication, WebSocket support and persistent volumes:
cd deployment
cp .env.example .env
# set DFLT_USERNAME / DFLT_PASSWORD (single user), or mount a custom htpasswd
docker-compose up -d
# app at http://localhost
Full documentation, including multi-user htpasswd management, HTTPS setup and troubleshooting, is in deployment/README.md.
For enterprise-grade auth — MFA/2FA, SSO, LDAP — the deployment/ stack also supports Authelia in front of nginx; see docs/NGINX_AUTHELIA_DEPLOYMENT.md and the Authelia section of the main README.
Behind any reverse proxy, remember to update the environment (example for imagetools.example.com):
OPENROUTER_APP_URL=https://imagetools.example.com
INSTANCE_URL=https://imagetools.example.com
CORS_ORIGINS=https://imagetools.example.com
And give nginx a generous body size and timeouts for image work:
server {
listen 443 ssl;
server_name imagetools.example.com;
client_max_body_size 20M;
location / {
proxy_pass http://localhost:8082;
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;
proxy_read_timeout 600s;
}
}
Notes & requirements
- Docker 20.10+ and Docker Compose 2.0+; 2 GB+ RAM recommended for image processing (background-removal models), ~2.5 GB image size.
- Startup is typically 5–15 seconds (database init + model load); the compose healthcheck waits with a 40 s start period.
- Back up the
imagetools-storagevolume — it holds uploads and the SQLite database. - SQLite is file-based: fine for a single container; consider PostgreSQL for multi-replica setups (requires code changes).
- Update by pulling latest code (or the next image tag) and
docker-compose up -dagain; verify withcurl http://localhost:8082/health.
See docs/DOCKER_DEPLOYMENT.md for the complete guide: multi-stage build details, OAuth setup, backup/restore, troubleshooting and performance tuning.