DEV Community

Cover image for My requirements.txt Has Two Lines. Here's Why That Sold 39 Products to Strangers."**

My requirements.txt Has Two Lines. Here's Why That Sold 39 Products to Strangers."**

My requirements.txt Has Two Lines. Here's Why That Sold 39 Products to Strangers.

I still remember the night my upload batch ran twice. I woke up to a flagged Adobe Stock account, three CSVs with mismatched headers sitting in a folder called output_final_v2, and 39 products that I'd already packaged but couldn't prove I'd posted. I'd spent two weeks building a pipeline and zero minutes building a ledger. The art was fine. The plumbing was not.

That night is why requirements.txt in the kit I eventually shipped has exactly two lines:

Pillow
reportlab
Enter fullscreen mode Exit fullscreen mode

That's it. Not a dozen dependencies, not a framework, not a cloud SDK. Two packages, both doing exactly one job, and everything else running on the standard library. That constraint wasn't aesthetic. It was survival on a 1 vCPU, 2 GB machine with no GPU.

The problem was never the art

Making the art was the fun part. The four things that actually stalled me were boring:

  1. Metadata. Adobe wants a 5-column CSV, Vecteezy wants 4, Dreamstime wants 15. Every one of them rejects a wrong header instantly, and a rejection on a batch of 200 files is a bad afternoon.
  2. Double-posting. Running a batch twice is the fastest way to get an account flagged. This is the mistake I made.
  3. Turning a doc into a product. You have the content. You need it to look like something someone would pay for.
  4. Packaging. A zip with a real cover sells. A bare file does not.

So the kit became four scripts, each aimed at one of those stalls. make_metadata.py writes the exact header each platform demands and nothing else. upload_tracker.py is a ledger that remembers what you already posted, so a re-run never double-posts. md2pdf.py turns a Markdown file into a styled PDF with headings, tables, lists and links. pack_product.py zips the files and renders a 1280×720 cover plus a 600×600 thumbnail.

Two of those four scripts install nothing. make_metadata.py and upload_tracker.py use the standard library alone. If metadata and tracking are your only bottleneck, you don't even run pip.

Two dependencies is a design decision, not a flex

Pillow draws the covers. reportlab writes the PDF. That's the whole dependency surface, and it exists because each one is load-bearing. I didn't add a CLI framework because argparse works. I didn't add a templating engine because string formatting works. On 1 vCPU with 2 GB of RAM, every import is a tax you pay on every subprocess.

The pipeline that packaged 39 products, 856 MB of output, ran inside those limits. The parent project behind it is roughly 19,000 lines of pipeline code and 300+ pages of research, all of it validated on the same tiny box. None of that size leaked into the shipped kit. The kit is four scripts. The research became the rules the scripts enforce, and the rules are what you actually benefit from.

The habits that let you run on a potato

Running real workloads on 1 vCPU and 2 GB is not about clever code. It's about a few habits I had to learn the hard way:

  • One process at a time. Never fan out image jobs in parallel on a single core. You don't go faster. You swap.
  • Free memory between items. Call gc.collect() after each asset.
  • Measure peak RSS, not average. resource.getrusage(RUSAGE_SELF).ru_maxrss tells you the real ceiling. Averages hide the spike that kills you.
  • One file per subprocess for heavy render steps, so memory returns to the OS instead of lingering.
  • Render at the resolution you need. Inspecting a preview at 60 dpi beats rendering it at 300 and wondering why the box is breathing hard.

None of this is exotic. It's just what happens when you stop pretending you have a GPU and start respecting the one core you rented.

The folder layout that keeps 39 products from becoming a junk drawer

A catalogue turns into a junk drawer without a fixed structure. The kit assumes one:

project/
  assets/     raw output, one file per asset
  output/     ready-to-submit files + metadata.csv
  packs/      finished product zips + covers
  research/   your notes and sources
  scripts/    these tools
Enter fullscreen mode Exit fullscreen mode

Keep research/ forever. Your scrape logs and notes are the raw material for the next product. Deleting them to "clean up" is the mistake this project made once and won't repeat. That's a direct quote from my own notes after I cleared a folder I needed three weeks later.

What actually sold 39 products

Not the scripts alone. The scripts made the volume possible. A consistent cover, a correct CSV, and a ledger that proved I hadn't double-posted meant I could ship 39 finished products instead of 12 half-finished ones. The "to strangers" part matters. None of those 39 went to people who knew me. They went to people who found a thumbnail, clicked, and bought because the packaging looked like a product and the metadata got past the gatekeepers.

The full pipeline sits under a free stack: an orchestrator that schedules jobs and retries failures, a local proxy for LLM access, and the packaging layer these four scripts provide. The kit is the part I could hand to someone else without a twelve-step setup guide, because there's nothing to set up beyond pip install -r requirements.txt.

If you want the scripts themselves, the same ones that packaged 39 products and ran on a single-core 2 GB server, that's what the kit is. Not a tutorial that ends at "here's how you'd do it." The working tools, with the rules written in.

Get digital asset factory


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

Top comments (0)