Self-hosted, open-source image processing platform with 30+ tools. Runs in a single Docker container. Resize, compress, convert, remove backgrounds, upscale, run OCR, and more — without sending images to external services.
Core image processing library built on Sharp. Handles all non-AI operations: resize, crop, rotate, flip, convert, compress, strip metadata, and color adjustments. No network dependencies, runs entirely in-process.
### @stirling-image/ai
Bridge layer that calls Python scripts via child processes. Each AI capability has a TypeScript wrapper that spawns a Python subprocess, passes image data through the filesystem, and returns the result.
Supported operations:
- Background removal — BiRefNet-Lite model via rembg
- Upscaling — RealESRGAN
- OCR — PaddleOCR
- Face detection/blurring — MediaPipe
- Object erasing (inpainting) — LaMa Cleaner
Python scripts live in `packages/ai/python/`. The Docker image pre-downloads all model weights during the build so the container works offline.
### @stirling-image/shared
Shared TypeScript types, constants (like APP_VERSION and tool definitions), and i18n translation strings used by both frontend and backend.
## How a request flows
1. The user picks a tool in the web UI and uploads an image.
2. The frontend sends a multipart POST to `/api/v1/tools/:toolId` with the file and settings.
3. The API route validates the input with Zod, auto-orients the image based on EXIF metadata, then calls the appropriate package function.
4. For AI tools, the TypeScript bridge spawns a Python subprocess, waits for it to finish, and reads the output file.
5. The API returns a `jobId` and `downloadUrl`. The frontend can poll `/api/v1/jobs/:jobId/progress` via SSE for real time status.
6. The user downloads the processed image from `/api/v1/download/:jobId/:filename`.
---
# Configuration
All configuration is done through environment variables. Every variable has a sensible default.
## Server
| Variable | Default | Description |
|---|---|---|
| PORT | 1349 | Port the server listens on |
| RATE_LIMIT_PER_MIN | 100 | Maximum requests per minute per IP |
## Authentication
| Variable | Default | Description |
|---|---|---|
| AUTH_ENABLED | false | Set to true to require login. Docker image defaults to true. |
| DEFAULT_USERNAME | admin | Username for the initial admin account. Only used on first run. |
| DEFAULT_PASSWORD | admin | Password for the initial admin account. Change after first login. |
## Storage
| Variable | Default | Description |
|---|---|---|
| STORAGE_MODE | local | local or s3. Only local is currently implemented. |
| DB_PATH | ./data/stirling.db | Path to the SQLite database file |
| WORKSPACE_PATH | ./tmp/workspace | Directory for temporary files during processing |
| FILES_STORAGE_PATH | ./data/files | Directory for persistent user files |
## Processing limits
| Variable | Default | Description |
|---|---|---|
| MAX_UPLOAD_SIZE_MB | 100 | Maximum file size per upload in megabytes |
| MAX_BATCH_SIZE | 200 | Maximum number of files in a single batch request |
| CONCURRENT_JOBS | 3 | Number of batch jobs that run in parallel |
| MAX_MEGAPIXELS | 100 | Maximum image resolution allowed |
## Cleanup
| Variable | Default | Description |
|---|---|---|
| FILE_MAX_AGE_HOURS | 24 | How long temporary files are kept before deletion |
| CLEANUP_INTERVAL_MINUTES | 30 | How often the cleanup job runs |
## Appearance
| Variable | Default | Description |
|---|---|---|
| APP_NAME | Stirling Image | Display name shown in the UI |