SnapOtter logo

SnapOtter

Self-hosted file-processing suite for images, video, audio, PDFs, and documents

Alternative to: cloudconvert, smallpdf, convertio


SnapOtter is an open-source, self-hosted file-processing suite offering 200+ tools across image, video, audio, PDF, and document files, accessible through a web UI, REST API, and pipelines. It bundles conversion, compression, OCR, transcription, and local AI features (background removal, upscaling, photo restoration) so files never leave the user's network, replacing tools like CloudConvert, Smallpdf, and TinyPNG.

SnapOtter Docker Compose example

Self-host SnapOtter on your own server, homelab, or VPS starting from this Docker Compose example. It runs SnapOtter in Docker containers using the official snapotter/snapotter:latest, postgres:17-alpine, redis:8-alpine images, with persistent volumes and automatic restarts preconfigured. Review the environment variables and adjust them to your setup, save the file as compose.yml (or docker-compose.yml), and start the stack with docker compose up -d.

services:
  snapotter:
    image: snapotter/snapotter:latest
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - data:/data
      - workspace:/tmp/workspace
    environment:
      # Require login before any tool can be used.
      AUTH_ENABLED: "true"
      # Username of the initial administrator account.
      DEFAULT_USERNAME: "admin"
      # Password of the initial administrator account. CHANGE THIS for any non-local deployment.
      DEFAULT_PASSWORD: "admin"
      # Skip the forced password change on first login (set true only if you already set a strong DEFAULT_PASSWORD).
      SKIP_MUST_CHANGE_PASSWORD: "false"
      # Maximum upload size in MB per file (0 = unlimited).
      MAX_UPLOAD_SIZE_MB: "0"
      # Maximum number of files per batch job (0 = unlimited).
      MAX_BATCH_SIZE: "0"
      # Maximum image size in megapixels to process (0 = unlimited).
      MAX_MEGAPIXELS: "0"
      # Number of jobs processed concurrently (0 = auto, based on CPU count).
      CONCURRENT_JOBS: "0"
      # Worker threads per job (0 = auto, based on CPU count).
      MAX_WORKER_THREADS: "0"
      # Per-job processing timeout in seconds (0 = no timeout).
      PROCESSING_TIMEOUT_S: "0"
      # Maximum number of steps allowed in a single pipeline.
      MAX_PIPELINE_STEPS: "20"
      # API rate limit per client per minute.
      RATE_LIMIT_PER_MIN: "1000"
      # Maximum number of user accounts (0 = unlimited).
      MAX_USERS: "0"
      # Login session duration in hours.
      SESSION_DURATION_HOURS: "168"
      # Which peers may be trusted for X-Forwarded-For. Set to "true" only if a proxy you control sits in front on a public address.
      TRUST_PROXY: "loopback,linklocal,uniquelocal"
      # Runtime database connection (least-privilege role, DML only). The role is created and granted at boot using DATABASE_MIGRATION_URL.
      DATABASE_URL: "postgres://snapotter_app:snapotter_app@postgres:5432/snapotter"
      # Privileged database connection, used only at boot for migrations and role grants.
      DATABASE_MIGRATION_URL: "postgres://snapotter:snapotter@postgres:5432/snapotter"
      # Redis connection used for the job queue and caching.
      REDIS_URL: "redis://:snapotter@redis:6379"
    # Shared memory for image/video processing pipelines.
    shm_size: "2gb"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
      interval: 30s
      timeout: 5s
      start_period: 60s
      retries: 3

  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: "snapotter"
      # Password for the privileged database user. CHANGE THIS for any non-local deployment.
      POSTGRES_PASSWORD: "snapotter"
      POSTGRES_DB: "snapotter"
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 15s

  redis:
    image: redis:8-alpine
    command: >-
      redis-server
      --maxmemory-policy noeviction
      --maxmemory 512mb
      --appendonly yes
      --requirepass snapotter
    volumes:
      - redisdata:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "snapotter", "--no-auth-warning", "ping"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 10s

volumes:
  data:
  workspace:
  pgdata:
  redisdata:

Prefer a managed setup? WinterFlow installs, configures, and updates SnapOtter for you using this same Docker Compose configuration.