ConvertX will install and run on a 1 vCPU, 1 GB virtual machine, and it will convert a 2 MB PNG to WebP in a second. That box will also kill a 4K video conversion, a 300 MB PDF and a large EPUB rebuild, usually with no visible error at all. The honest hardware floor for general use is 2 vCPU and 4 GB of RAM with 3 times your largest file free on disk, and the floor for routine video work is 4 vCPU and 8 GB. If your real workload is long video transcodes or 500 MB office documents, and you have no appetite for reading container logs, a hosted converter will fail less than a small self-hosted instance.
TL;DR by reader profile:
- The occasional image converter, for example someone flattening HEIC holiday photos to JPEG once a month: self-host ConvertX on the smallest tier you have, because single image jobs finish in seconds and memory pressure never builds.
- The document and ebook person, for example a researcher pushing DOCX and EPUB through Pandoc and Calibre weekly: self-host on 2 vCPU and 4 GB, because LibreOffice and Calibre are single threaded and want headroom rather than cores.
- The video converter, for example a parent re-encoding phone clips to H.264 for a TV: budget 4 vCPU, 8 GB and a large scratch volume, or accept conversions measured in hours.
- The privacy-driven user with sensitive files, for example someone converting bank statements and medical scans: self-host even on modest hardware, because the failure mode is a slow job, not a document sitting on a third party server.
- The shared household or small team instance, for example five people with one link: treat concurrency as the real constraint, because two overlapping jobs on 4 GB will OOM before either one finishes.
-
The reader with no sysadmin appetite at all, for example someone who cannot read
docker logsand does not want to: use a hosted converter for anything over 100 MB, because silent failure with no diagnosis is worse than a public upload you understand.
The central tradeoff is this: ConvertX moves your files off other people's servers, but it also moves every timeout, memory limit and disk exhaustion onto a machine you now have to size correctly yourself.
Table of contents
- What happens inside ConvertX when you press Convert
- What are the real hardware floors for ConvertX, tier by tier
- The upload path: where large files die before ConvertX ever sees them
- How do you tell an OOM kill from a timeout in ConvertX
- Why does a ConvertX job finish with no output and no error
- Scratch disk: the hidden multiplier on every ConvertX conversion
- Concurrency in ConvertX: what breaks when two jobs overlap
- Video and FFmpeg in ConvertX: the workload that decides your tier
- Images and ImageMagick in ConvertX: pixel maths, memory limits and policy files
- Documents, ebooks and LibreOffice in ConvertX: slow, single threaded, quietly fragile
What happens inside ConvertX when you press Convert
ConvertX is not one converter. It is a Bun web application that receives your upload, writes it to disk, then shells out to whichever command line tool handles that format pair. Understanding that chain is the whole hardware question, because the web layer costs almost nothing and the child process costs everything.
- The upload lands on disk first: your file is written into the container's data volume before any conversion starts, so a 500 MB video occupies 500 MB before a single frame is encoded.
- A converter binary is selected by format pair: FFmpeg for audio and video, ImageMagick and libvips for raster images, Inkscape and resvg for vectors, Calibre for ebooks, Pandoc and LibreOffice for documents, Assimp for 3D meshes. Each has its own memory behaviour and none of them know about the others.
- The job runs as a child process, not inside the web server: ConvertX waits for that process to exit. If the kernel kills it, ConvertX sees a dead process, not an explanation.
- The output is written beside the input: peak disk usage is input plus output plus whatever temporary files the tool creates, which for LibreOffice and Calibre is substantial.
-
Results persist until cleanup runs:
AUTO_DELETE_EVERY_N_HOURSdefaults to 24, so a day of conversions accumulates on the volume before anything is reclaimed.
The practical consequence: ConvertX itself idles at a few hundred megabytes. Your 8 GB requirement is not ConvertX, it is FFmpeg encoding 4K, or ImageMagick decompressing a 200 megapixel TIFF into an uncompressed pixel buffer. Size the box for the worst binary you will ever invoke, not for the web interface.
What are the real hardware floors for ConvertX, tier by tier
Pick your tier from the heaviest format you actually convert, not from the average one. The table below maps common hardware to what genuinely completes and what starts failing.
| Tier | Completes reliably | Starts failing at |
|---|---|---|
| 1 vCPU, 1 GB RAM | Single images under 20 megapixels, PDF to text, small Markdown and DOCX via Pandoc, audio transcodes | Any video over roughly 2 minutes, LibreOffice on large spreadsheets, two concurrent jobs of any kind |
| 2 vCPU, 4 GB RAM | Batches of 20 photos, EPUB and MOBI via Calibre, 1080p clips under 5 minutes, most office documents | 4K video, TIFF files over 100 megapixels, three or more overlapping users |
| 4 vCPU, 8 GB RAM | 1080p video at usable speed, 4K short clips, large PDF rasterisation, 2 to 3 concurrent jobs | Long 4K transcodes measured in hours, batch video, sustained multi user load |
| 8 vCPU, 16 GB RAM | Feature length video, large batch image work, several simultaneous users | Little in practice, the constraint becomes disk throughput and patience |
Disk matters as much as RAM. Reserve at least 3 times the size of your largest file, and remember that with AUTO_DELETE_EVERY_N_HOURS at its default of 24, a day of jobs stays resident.
Where that hardware lives is a separate decision. A self managed VPS, an always on home server, a NAS running Docker and a managed option all reach the same floors. Yundera is a managed Personal Cloud Server, built on CasaOS, that runs self-hosted apps as Docker containers on a server dedicated to the user. Whichever you choose, the tier table above is what decides whether a conversion finishes.
The upload path: where large files die before ConvertX ever sees them
Half of the "ConvertX is broken" reports are not ConvertX at all. The file never reached the container. Every layer between your browser and port 3000 has its own size ceiling, and most of them reject silently or with a generic 413.
-
Nginx caps uploads at 1 MB by default:
client_max_body_sizestarts at1m. Until you raise it, anything larger returns 413 Request Entity Too Large and ConvertX logs nothing, because no request arrived. Set it to a value above your largest file, or to0to disable the check. - Cloudflare's free proxy caps request bodies at 100 MB: if your domain is proxied through Cloudflare on a free plan, a 400 MB video cannot reach your server regardless of your own configuration. Grey clouding the record or using a direct hostname is the only fix.
-
Proxy read timeouts kill slow uploads: nginx defaults
proxy_read_timeoutto 60 seconds. A large file over a slow home uplink can exceed that before the body finishes. - Your upstream bandwidth is the real limit at home: a 10 Mbit upload link moves roughly 1.25 MB per second, so a 500 MB file takes over 6 minutes of held connection before conversion even begins.
- Caddy and Traefik behave differently: Caddy applies no default body limit, so people migrating from nginx often see the ceiling vanish without understanding why.
Where you run the instance changes which of these you have to configure. A self managed VPS means editing the proxy yourself, a NAS exposes its own reverse proxy settings, and on Yundera each app is reachable on a public HTTPS subdomain via NSL.SH mesh routing. Test with a file at your true maximum size before trusting any of it.
How do you tell an OOM kill from a timeout in ConvertX
These two produce almost identical symptoms in the browser: a job that never completes. They need opposite fixes. More RAM does nothing for a timeout, and a longer timeout does nothing for an OOM kill. Diagnose before you resize.
| Signal | Points to an OOM kill | Points to a timeout |
|---|---|---|
Exit status in docker logs convertx
|
Process terminated by signal 9, container exit code 137 | Process still running, or killed cleanly after a fixed interval |
docker inspect --format '{{.State.OOMKilled}}' convertx |
Returns true after the container itself dies |
Returns false, the container is healthy |
Host kernel log via dmesg -T
|
Contains "Out of memory: Killed process" naming ffmpeg, soffice or convert | Contains nothing at all |
| Browser behaviour | Connection stays open, then the job vanishes from progress | 504 Gateway Timeout from the reverse proxy while the job continues server side |
docker stats during the job |
Memory climbs to the limit then the row disappears | Memory sits flat, CPU pegged at 100 percent on one core |
The pattern worth memorising: an OOM kill is sudden and leaves kernel evidence, a timeout is patient and leaves the converter alive. A frequent surprise is a job that hits both, where FFmpeg keeps encoding for 40 minutes after the proxy already returned 504, so the output file appears later with no matching entry in your browser.
Add swap before you upgrade the tier. On a 4 GB box, 2 GB of swap turns some ImageMagick kills into slow but successful conversions. It will not save a 4K transcode, and a job that swaps heavily for 20 minutes is usually a signal that the tier is wrong rather than the configuration.
Why does a ConvertX job finish with no output and no error
This is the failure mode that drives people back to hosted converters. The job appears in the history, the status does not scream at you, and the download is either missing or a 0 byte file. ConvertX only knows what the child process told it, and command line converters lie about success constantly.
- The converter exited 0 but wrote nothing: LibreOffice is the worst offender here. It returns success after refusing to convert a document it could not parse, so ConvertX records a finished job pointing at an empty file.
-
A stale LibreOffice profile lock: if a previous conversion was killed mid run, the leftover profile directory blocks the next invocation. The fix is restarting the container, which is why the same document fails five times then works after
docker restart convertx. - Missing fonts or unsupported subfeatures: a DOCX with an embedded font, or a PDF using a CJK typeface the container lacks, converts to a page of blank boxes rather than an error.
-
The output was cleaned up before you fetched it: with
AUTO_DELETE_EVERY_N_HOURSat 24, a job you started last night and downloaded this evening can be gone. - Disk filled mid write: the converter writes a partial file, then fails on the final flush. You get a truncated result and a job marked done.
Your only reliable diagnostic is docker logs convertx, ideally with --tail 200 right after the failure. If reading container logs to work out why a PDF came back blank sounds like a bad evening, that is a legitimate reason to keep a hosted converter for anything important.
Scratch disk: the hidden multiplier on every ConvertX conversion
RAM gets the attention, disk causes the outages. ConvertX needs room for the input, the output and whatever intermediate the converter invents along the way, all at the same moment. That peak is routinely 5 to 10 times the size of the file you uploaded.
- Rasterising a PDF is arithmetic, not magic: one A4 page at 300 DPI in RGB is about 3508 by 2480 pixels, roughly 26 MB uncompressed. A 100 page document becomes around 2.6 GB of intermediate data before anything is written as PNG or JPEG.
- Uncompressed video intermediates are enormous: a single 1080p RGB frame is 1920 by 1080 by 3 bytes, about 6.2 MB. At 25 frames per second that is 155 MB of scratch per second of footage if a lossless intermediate is produced.
- Calibre and LibreOffice unpack before they convert: an EPUB or DOCX is a zip archive, and the expanded working directory plus embedded images can dwarf the original file.
-
Everything accumulates in the same volume: uploads, outputs and
mydb.sqliteshare/app/data. If you did not mount a named volume, all of it lands on the container layer and fills the host root partition instead. - Retention multiplies the total: at the default 24 hour cleanup, a day of 20 conversions averaging 200 MB in and 150 MB out holds roughly 7 GB before anything is reclaimed.
Check with df -h on the host and docker system df for volume usage, not by guessing from your upload sizes. A full disk produces truncated outputs rather than clean errors, so a 20 GB volume on a video oriented instance is a floor, not a comfortable allocation.
Concurrency in ConvertX: what breaks when two jobs overlap
ConvertX has no job queue that serialises heavy work. A batch upload of 12 files, or two people clicking Convert at the same moment, means multiple converter processes competing for the same cores and the same memory ceiling. This is the single most common reason a box that felt adequate suddenly fails.
-
FFmpeg claims every core by default: with no explicit
-threadsvalue it scales to all available CPUs. On 4 vCPU, two simultaneous encodes request 8 threads across 4 cores, so both run at roughly half speed plus context switching overhead, and neither finishes when you expect. - ImageMagick multiplies memory, not just time: each process holds its own pixel buffer. Two 100 megapixel TIFF conversions on a 4 GB box are not one job twice, they are two independent allocations racing the same limit.
-
LibreOffice does not like company: two
sofficeinvocations contending for the same user profile directory produce one success and one job that returns nothing usable. -
SQLite serialises writes:
mydb.sqlitehandles job records fine at household scale, but it is a single writer database, not something to plan a 20 user instance around. - The web interface stays responsive while everything else starves: Bun keeps serving pages, so the instance looks healthy while conversions crawl. Health checks tell you nothing here.
The practical control is Docker resource limits. Setting cpus: "3.0" and mem_limit: 6g on a 4 vCPU, 8 GB host leaves the operating system breathing room and makes the failure predictable rather than random. If your instance serves more than three people who convert video, plan for one job at a time and tell them so.
Video and FFmpeg in ConvertX: the workload that decides your tier
If you never convert video, ignore most of the hardware advice in this article and run ConvertX on whatever you have. If you do, video alone sets your tier, because encoding is the only workload here that saturates every core for a sustained period.
| Source material | On 2 vCPU, 4 GB | On 4 vCPU, 8 GB |
|---|---|---|
| Audio only, MP3 to FLAC or WAV | Completes in seconds, no real load | Identical, the tier is irrelevant |
| 1080p H.264 clip under 5 minutes | Completes, slower than realtime, tolerable | Completes comfortably, cores are the binding constraint |
| 1080p H.264 to H.265 | Encoding runs far slower than the equivalent x264 job, expect to leave it running | Usable, still the slowest common conversion |
| 4K to 1080p, 10 minutes or longer | Frequently exceeds proxy patience and sometimes memory | Completes, but plan for an unattended run |
| Feature length or batch video | Not a realistic workload | Marginal, this is where you want 8 vCPU |
Three facts explain the whole table. A 4K frame carries 8.3 million pixels against 2.1 million at 1080p, so the same encoder does roughly four times the work per frame. The x264 preset ladder from ultrafast to veryslow spans an enormous speed range at similar quality targets, and ConvertX exposes this through the FFMPEG_ARGS environment variable, so setting -preset veryfast is the cheapest tier upgrade available. Finally, the container has no GPU acceleration unless you explicitly pass a device through, so everything runs on the CPU.
Set FFMPEG_ARGS before you buy more vCPU. A preset change costs nothing and often moves a job from abandoned to finished.
Images and ImageMagick in ConvertX: pixel maths, memory limits and policy files
Image conversion looks harmless because the files are small. The decoded buffer is what matters, and that number has nothing to do with the size on disk. A 12 MB JPEG and a 12 MB PNG can consume wildly different amounts of RAM.
- Work out the buffer, not the file size: ImageMagick is normally built at Q16, meaning 2 bytes per channel. RGBA at 16 bits is 8 bytes per pixel, so a 100 megapixel scan needs roughly 800 MB of memory before any processing begins. That alone explains most failures on a 1 GB box.
- The pixel cache silently falls back to disk: when the memory limit is reached, ImageMagick starts using a disk backed cache rather than failing. The job still completes, hundreds of times slower, which is why one photo occasionally takes 20 minutes while its neighbours take 2 seconds.
-
policy.xmlcan refuse the conversion outright: Debian based images ship with the PDF, PS and EPS coders disabled. The giveaway in the logs is "attempt to perform an operation not allowed by the security policy". That is a policy decision, not a hardware problem, and adding RAM will never fix it. - The same file has a cheap path and an expensive one: ConvertX also bundles libvips, which streams in tiles rather than loading the full image. Where both can handle a format pair, the vips route uses a fraction of the memory.
- Batches compound the buffer, not the file count: 20 photos at 24 megapixels is not 20 small jobs, it is repeated 400 MB allocations.
Check a suspicious file with identify -verbose before blaming the tier.
Documents, ebooks and LibreOffice in ConvertX: slow, single threaded, quietly fragile
Document conversion is the workload people assume is trivial and then cannot explain. It rarely triggers an OOM kill on 4 GB. It just takes far longer than the file size suggests, and it does not get faster when you add cores.
- Which binary handles the job decides everything: Pandoc converting DOCX to Markdown or HTML is a text transformation that finishes almost immediately on any tier. The same DOCX to PDF goes through LibreOffice or XeLaTeX, which is a completely different order of cost.
-
LibreOffice pays a cold start on every single job: each conversion launches
sofficein headless mode, which means loading an entire office suite before the first page is laid out. That fixed overhead dominates on small documents, so a 40 KB letter and a 4 MB report can take a similar time. - Layout is single threaded: repagination, table reflow and font metrics run on one core. A 4 vCPU box converts a large spreadsheet no faster than a 1 vCPU box, which is why upgrading the tier for document work is usually wasted money.
- LaTeX routes run multiple passes: XeLaTeX processes a document two or three times to resolve the table of contents and cross references, so the wall clock is a multiple of one pass, not one pass.
- Calibre scales with embedded images, not page count: a 600 page text only EPUB converts easily, while a 90 page illustrated PDF to EPUB decodes every image and needs far more headroom.
The useful move is choosing your target format deliberately. If HTML or Markdown will do, you avoid LibreOffice and LaTeX entirely and turn a minutes long job into a seconds long one.
Top comments (0)