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.

EndpointURL (default compose)
Web UIhttp://localhost:8082
APIhttp://localhost:8082/api/v1/
API docs (Swagger)http://localhost:8082/api/v1/docs
Health checkhttp://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.

VariableDefault in composeWhy 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.
Production checklist Set a strong 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-storage volume — 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 -d again; verify with curl 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.