# SnapOtter: a self-hosted file-processing stack for images, video, audio and PDFs

> SnapOtter bundles 200+ conversion, compression, OCR and local AI tools into one Docker deployment with a REST API and pipelines. It fits teams that cannot send files to a SaaS converter, and it is a poor fit if you only need PDF work or want a hosted service.

**snapotter-hq/SnapOtter** — Project brief: Open-source, self-hosted file-processing tool. Convert, compress, OCR, transcribe & run local AI across image, video, audio, PDF & documents, via UI, REST API & pipelines. Your files never leave your network.

- Repository: https://github.com/snapotter-hq/SnapOtter
- Website: https://snapotter.com
- Stars: 2,755 · Forks: 154
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/snapotter-hq-snapotter

## What SnapOtter replaces, and for whom

The README positions SnapOtter as a replacement for CloudConvert, Smallpdf, TinyPNG, TinyWow and Otter.ai, and it is explicit about the reason: files stay on infrastructure you own. That framing matters more than the tool count. The project is aimed at people who already accept the operational cost of running a server because the alternative, uploading a client's contract or a patient's scan to a third-party converter, is not acceptable to them.

The scope is unusual. Image, video, audio, PDF and generic file tools sit behind one deployment, and the README counts them as 107 image tools, 57 video tools, 27 audio tools, 29 PDF tools and 23 file tools. A single stack covering all five is the pitch against narrower projects: the README says Stirling-PDF stops at PDFs and ConvertX stops at conversions. If your workload is genuinely mixed, that consolidation is the argument. If it is not, the breadth is weight you carry for nothing.

One thing the README does not do is describe a team size or a deployment scale. It describes a Docker stack and a UI, and leaves sizing to you.

## How the stack is put together

Two deployment shapes are documented. The quick start is a single container with embedded Postgres 17 and Redis 8. The production shape is a three-container Compose stack: the app, Postgres 17, and Redis 8 as separate services.

The database configuration is the most interesting detail in the README. The app connects with two URLs. DATABASE_URL points at a role that can only read and write rows, and DATABASE_MIGRATION_URL points at the owner, which connects only during boot to migrate and to grant permissions. That is a deliberate split, and it means a compromised request path does not hold DDL rights over your database. It also means both URLs must be correct at start-up; the owner credentials are needed every time the container boots, not just on the first run.

Processing itself runs through native media and document engines. The .env.example lists binary overrides for FFMPEG_PATH, FFPROBE_PATH, QPDF_PATH, SOFFICE_PATH and PDFCPU_PATH, with $PATH lookup as the default. That tells you the app shells out to established tools rather than reimplementing codecs, which is the sensible choice for format coverage. It also tells you where a failure will surface when a format misbehaves.

AI work is pooled separately. MAX_AI_JOBS_PER_USER defaults to 5, and the comment in .env.example states the AI pool has concurrency 1. So AI tools queue behind each other even when ordinary conversion jobs are running in parallel.

## Installing SnapOtter and running a first conversion

The quick start is one command. It publishes port 1349, mounts a named volume at /data, and pulls the latest tag.

```bash
docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data snapotter/snapotter:latest
```

After that, the README says to open http://localhost:1349 and log in with admin / admin. You will be asked to change the password on first login, so do that before you expose the port to anything.

For production the README gives a Compose file with the app, Postgres 17 and Redis 8, and the two database URLs described above. Save it as compose.yaml and start it:

```bash
docker compose up -d
```

The environment block uses ${POSTGRES_APP_USER:-snapotter_app} style defaults, so the stack starts without a .env file but you should set real values before it holds anything you care about.

The REST API is the second entry point. Every tool is available through it with API key auth, and interactive docs are served at /api/docs. That page is the place to confirm the exact request shape for the tool you want, rather than guessing from the UI. Pipelines chain tools into reusable workflows, import and export as JSON, and default to 20 steps via MAX_PIPELINE_STEPS.

## Limits you will hit: batch size, SVG caps and GPU support

The published image and a source build do not behave the same. The README states that batch size is unlimited in the published image and 100 from a source build, controlled by MAX_BATCH_SIZE. If you build from source to patch something, you silently inherit a ceiling that the container you were testing against did not have. Check the value in your own image before you promise a batch workflow to anyone.

SVG handling has a trap worth reading twice. The .env.example notes that SVG has no auto sizing, so setting MAX_SVG_SIZE_MB=0 disables the pre-parse size cap entirely, and it ships a code default of 50 MB so that copying the example file does not remove the guard. Copying .env.example and then zeroing values is a normal habit, and here it removes a protection rather than relaxing a limit.

Hardware acceleration is narrower than the AI feature list suggests. NVIDIA CUDA is supported for background removal, upscaling and transcription through a separate GPU Compose file. Intel and AMD integrated GPU acceleration through VA-API, Quick Sync or OpenCL is not supported for AI inference, and those systems run AI tools on CPU. OCR is deliberately CPU-only on both CPU and NVIDIA hosts, using the same portable runtime. The README also points to the Docker Tags page for the GPU Compose example and benchmarks, which is where you should look before sizing a box.

There is a related caveat in .env.example about SNAPOTTER_HW_ACCEL. The shipped static ffmpeg has neither nvenc nor vaapi, so hardware encoding falls back to software and says so at startup unless you point FFMPEG_PATH at your own build. The setting alone does not buy you acceleration.

## SnapOtter against a PDF-only self-hosted tool

The closest comparison the README itself makes is Stirling-PDF, which it describes as stopping at PDFs. The difference is not quality, it is the shape of the deployment. A PDF-only tool gives you one container, one job, and a smaller surface to reason about. SnapOtter gives you PDF merging, splitting, compression, redaction, signing, watermarking and OCR alongside video transcoding and audio transcription, which means the same process tree, the same queue and the same resource limits are shared across very different workloads.

That sharing cuts both ways. A long video transcode and a PDF OCR job compete for the same worker pool, and MAX_WORKER_THREADS, PROCESSING_TIMEOUT_S and SUBPROCESS_MEMORY_LIMIT_MB are the knobs that decide how that contention resolves. With a PDF-only tool you never have that conversation. With SnapOtter you do, and the defaults in .env.example are mostly 0, meaning auto or unlimited.

ConvertX is the other comparison the README draws, and it is described as stopping at conversions. If your requirement is a conversion endpoint and nothing else, a narrower tool is less to operate. SnapOtter is the right call when the modalities genuinely overlap in one workflow, for example a pipeline that extracts audio from a video, transcribes it, and attaches the transcript to a PDF.

## Licence, upgrades and what each release costs you

SnapOtter is licensed under AGPL-3.0, and the repository also carries LICENSING.md, a CLA.md and THIRD_PARTY_NOTICES.md. AGPL-3.0 is a network copyleft licence: if you modify the software and let users interact with it over a network, the source of your modified version has to be offered to those users. Running the unmodified official image for internal file conversion is a different situation from shipping a modified SnapOtter as part of a product you expose to customers. I am not giving legal advice, and the licence text and LICENSING.md are the documents that decide your case.

The upgrade cadence is visible in the release list: v2.0.0 on 2026-07-07, v2.1.0 on 2026-07-10, and v2.2.0 on 2026-07-29. Three releases in one month is a fast-moving 2.x line, and the repository ships a MIGRATING.md plus an upgrading guide for 1.x to 2.0. The last push to the repository was on 2026-07-29, which is the date to weigh if you are deciding whether the project is being worked on right now.

Operationally, upgrades are the Compose path: pull the new image and bring the stack up. The database URL split means migrations run with owner credentials at boot, so a failed migration is a boot-time failure rather than a silent partial state. The repository layout, with .release-notes/ and a CHANGELOG.md, is where release-specific notes live.

## Telemetry, defaults and the first things to change

SnapOtter sends basic analytics by default. The README says they help catch bugs and improve tools, and that they can be disabled at build time with SNAPOTTER_ANALYTICS=off or at runtime through an in-app admin opt-out. There is a TELEMETRY.md in the repository and a dedicated docs section for it. If the reason you are self-hosting is that nothing leaves your network, this is the first setting to review, and the README is honest that the default is on.

The authentication defaults are the second. AUTH_ENABLED defaults to true, and DEFAULT_USERNAME and DEFAULT_PASSWORD are both admin in .env.example. The password change prompt on first login is the guard, but a container reachable from a network before anyone logs in is running with known credentials. OIDC and SSO are supported for Google, GitHub, Okta or any OpenID Connect provider if you want to skip local accounts entirely.

Third, storage and cleanup. STORAGE_MODE defaults to local, FILE_MAX_AGE_HOURS to 72, and CLEANUP_INTERVAL_MINUTES to 60. Files are deleted on a timer, which is good hygiene and also a data-retention policy you should confirm matches what your users expect. The repository also carries a signatures/ directory and a .trivyignore, which suggests image signing and vulnerability scanning are part of the release process, though the README does not document either.

## Conclusion

Adopt SnapOtter if you already run Docker, need several file modalities behind one API, and cannot let files leave your network. Do not adopt it if your work is PDF-only, or if you expect GPU acceleration on an Intel or AMD integrated GPU. Before rollout, verify the MAX_BATCH_SIZE and MAX_PIPELINE_STEPS values in your published image, confirm that your licence obligations under AGPL-3.0 fit how you expose the service, and change the default admin / admin credentials on first login.

## FAQ

### What is SnapOtter?

It is an open-source, self-hosted file-processing tool that converts, compresses, OCRs, transcribes and runs local AI across image, video, audio, PDF and document files. It is reachable through a UI, a REST API and pipelines, and the README states that files never leave your network.

### How do I install SnapOtter with Docker Compose?

The README gives a Compose file with three services: the app, Postgres 17 and Redis 8. Save it as compose.yaml and run docker compose up -d. The app listens on port 1349.

### What are the default SnapOtter login credentials?

The README lists admin for both username and password, and states you will be asked to change the password on first login.

### Does SnapOtter support NVIDIA GPU acceleration?

A GPU Compose file enables NVIDIA CUDA acceleration for background removal, upscaling and transcription. OCR deliberately uses the same portable CPU runtime on both CPU-only and NVIDIA hosts, and Intel or AMD integrated GPU acceleration is not supported for AI inference.

## Sources

- [Official documentation](https://snapotter.com)
- [Official README](https://github.com/snapotter-hq/SnapOtter#readme)
- [Project repository](https://github.com/snapotter-hq/SnapOtter)
- [Release notes](https://github.com/snapotter-hq/SnapOtter/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/snapotter-hq-snapotter
