> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openinary.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Processing

> Worker concurrency, retries, cleanup and the fixed encode limits

Video transformations run as background jobs. Everything here is optional: left alone, the worker sizes itself to the container.

```bash theme={null}
VIDEO_MAX_CONCURRENT=4        # omit to auto-detect
VIDEO_JOB_RETRY_MAX=3
VIDEO_JOB_CLEANUP_HOURS=24
```

## Variables

| Variable | Default | Description |
| - | - | - |
| `VIDEO_MAX_CONCURRENT` | auto | Jobs processed at once. See [auto-detection](#auto-detection) |
| `VIDEO_JOB_RETRY_MAX` | `3` | Attempts before a job is marked permanently failed |
| `VIDEO_JOB_CLEANUP_HOURS` | `24` | Age at which completed and failed jobs are purged from the database |
| `VIDEO_WORKER_POLL_INTERVAL_MS` | `1000` | How often the worker checks for new jobs. Lower picks up faster and costs CPU |
| `VIDEO_FFMPEG_THREADS` | CPUs minus 1 | Threads per FFmpeg process. The default leaves a core for the HTTP loop so encoding never starves the API |
| `VIDEO_FFMPEG_NICENESS` | `15` | Process priority, 0 to 19. Higher means lower priority |

Each worker spawns an FFmpeg process, so raising `VIDEO_MAX_CONCURRENT` past what the container can feed will exhaust CPU and memory rather than going faster.

## Auto-detection

Unset, concurrency is `min(RAM / 2 GB, effective CPUs, 16)`, with a floor of 1. Both inputs are cgroup-aware, so inside a container they are the limits you set, not the host's hardware.

**The CPU term usually decides.** The bundled Compose file defaults `DOCKER_CPU_LIMIT` to `2.0`, which caps auto-detection at 2 workers no matter how much RAM the machine has.

| RAM | Effective CPUs | Workers |
| - | - | - |
| 4 GB | 2 (default) | 2 |
| 8 GB | 2 (default) | 2 |
| 16 GB | 2 (default) | 2 |
| 8 GB | 4 | 4 |
| 16 GB | 8 | 8 |

For more workers, raise [`DOCKER_CPU_LIMIT`](https://docs.openinary.dev/configuration/docker-resources) alongside memory, or set `VIDEO_MAX_CONCURRENT` explicitly.

## Fixed limits

These are in code, not configurable, and they explain most surprises about video output.

| Behavior | Value | Notes |
| - | - | - |
| Automatic downscale | 720p max | Applies when the URL requests no explicit `w` or `h`. Cuts an 8K source by about 98% of its pixels, which is what makes it processable at all |
| Source size cap | 200 MB | A larger source fails the request rather than occupying a worker |
| FFmpeg timeout | 5 minutes | The process is killed past this and the job retries up to `VIDEO_JOB_RETRY_MAX` |
| Encoder preset | `ultrafast` | Throughput over compression ratio |
| Audio | Copied, never re-encoded | Audio quality always matches the source |

## Related

> [card] [Video Transformations](https://docs.openinary.dev/media-transformations/video-transformations.md)
>
> URL parameters for resizing, trimming and encoding.

> [card] [Upload & Cache Warming](https://docs.openinary.dev/media-transformations/upload-and-prewarm.md)
>
> Pre-generate video variants at upload time.

> [card] [Docker Resources](https://docs.openinary.dev/configuration/docker-resources.md)
>
> The CPU limit that caps worker auto-detection.

> [card] [Queue Jobs](https://docs.openinary.dev/api-reference/queue/jobs.md)
>
> Inspect, retry and cancel jobs through the API.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.