DEV Community

Cover image for `pip install pillow reportlab` And Nothing Else: What a 2 GB Server Taught Me About Dependency Bloat"**

`pip install pillow reportlab` And Nothing Else: What a 2 GB Server Taught Me About Dependency Bloat"**

I hit the wall at 2:40 on a Tuesday afternoon. My server, a 1 vCPU box with 2 GB of RAM and no GPU, had just killed an image render with an OOM error. I checked the process list and found the culprit: a "simple" automation stack I had installed three weeks earlier, pulling in 47 transitive dependencies, a task queue, an ORM, and a config library, all to do what four Python scripts do now. That failure is why my current pipeline has exactly two third-party packages. Here is what I learned about dependency bloat, and why less code survived where the framework did not.

The 2 GB constraint is a design tool, not a limitation

When your ceiling is 2 GB of RAM, every import is a decision. I started measuring peak RSS instead of average usage, using resource.getrusage(RUSAGE_SELF).ru_maxrss after each batch. The number that shocked me was how much of my memory went to libraries that were loaded at startup and used once per run. The task queue alone sat at a few hundred megabytes before it did any work.

The fix was not optimization. It was deletion. My metadata generation and upload tracking now run on the standard library alone. Zero installs. csv, json, pathlib, argparse. The scripts that do need help, image covers and PDF rendering, import exactly two packages: Pillow and reportlab. That is the whole requirements.txt.

On a 1-core box, I also stopped fanning out image jobs in parallel. One process at a time, gc.collect() between items, and heavy render steps in their own subprocess so memory returns to the OS. Inspecting previews at 60 dpi instead of 300 made a bigger difference than any library swap.

Metadata is where platforms reject you, so the CSV header is sacred

The first real money moment in this project was a $0.99 sale on Adobe Stock, from a catalogue that eventually reached 1,700 assets with 58 live on Adobe. Getting there taught me that the boring part, the CSV, is where accounts live or die.

Adobe wants a 5-column header. Vecteezy wants 4 columns. Dreamstime wants 15. Submit the wrong header and the rejection is instant, no partial credit. My first attempt at a "flexible" metadata module tried to normalize all three formats with a plugin system. It was over-engineered and still got headers wrong.

What works is one script per concern that writes the exact header each platform demands and nothing else. make_metadata.py takes a folder of files and emits the right CSV with --platform adobe, --platform vecteezy, or --platform dreamstime. No framework needed to write a correct header row.

Double-posting is the fastest way to get flagged

This one cost me a scare early on. Re-running a batch job that had partially completed meant re-submitting assets already under review. Stock platforms treat duplicate submissions seriously, and repeated duplicates can get an account flagged.

The fix is embarrassingly simple: a ledger. upload_tracker.py records what has been posted, with pending, done, and status commands. A re-run checks the ledger first. It is a text-backed state file, not a database, and it has prevented every double-post since. This is the kind of problem that a heavy framework "solves" with a job queue and a migration, when a 100-line script with the standard library solves it completely.

Packaging is what turns files into products

Making the art is the fun part. Selling it requires a zip, a cover, and a thumbnail. My covers render at 1280x720 and thumbnails at 600x600, which is what pack_product.py produces with Pillow. The PDF path, md2pdf.py, handles headings, tables, lists, and links with reportlab, turning a Markdown doc into a styled product file in one command.

Across 39 finished products and 856 MB of output, these four scripts never needed a fifth dependency. The parent pipeline is around 19,000 lines of code, and the packaging layer stayed small because the rules behind it came from 300+ pages of research into what each platform actually checks at review. You do not need a framework to enforce a rule. You need the rule written down and a script that follows it.

The folder layout matters more than the stack

One habit worth stealing: fix your directory structure before you scale. My layout is assets/ for raw output, output/ for submission-ready files plus metadata, packs/ for finished zips and covers, research/ for notes and sources, scripts/ for tools.

The mistake I made once and will not repeat: deleting research/ during a cleanup. Scrape logs and notes are the raw material for future products. Disk is cheap. Regenerating research is not.

Where this comes from

Everything above is backed by real numbers: 1,700 assets made, 58 live on Adobe, first sale at $0.99, all validated on 1 vCPU, 2 GB RAM, no GPU. I packaged the four scripts, the folder layout, and the RAM habits into a small kit called the Pipeline Starter Kit, and the full system built from this work is the digital asset factory. It is not a magic button. It is the same tools I run, with the platform rules baked in, so you can skip the OOM crash and the wrong CSV header and start where I am now.

Get digital asset factory


Building digital products? Free guides & kits for digital product sellers.

Top comments (0)