Printing from a phone is still the worst UX in the house. I built a phone-upload web portal for my family: nobody used it. Not once. Asking people to bookmark an internal URL and pick files through a mobile browser was too much, and honestly, fair.

Then it hit me: every person in my house already knows how to do exactly one technical thing reliably. Forward an email. So the printer got an email address.

My wife forwards permission slips from the school pickup line. My kid emails homework from college. The in-laws send documents from across town. Nobody has asked me to print anything in a month: which is the entire point of self-hosting. When I posted the setup to r/selfhosted it drew 1,800 upvotes and 178 comments in a day, and the questions from that thread are answered below.

The architecture

phone / laptop
     |  email with attachment
     v
your mail provider --> IMAP folder (via a mail filter)
     |
     v
print-poller container (polls once a minute)
     |
     +-- sender allowlist check -- fail? -> rejected folder + bounce reply
     +-- PDF / image            -- straight to lp
     +-- Word doc / spreadsheet -- headless LibreOffice -> PDF -> lp
     +-- no attachment          -- render email body -> PDF -> lp
     |
     v
CUPS --> driverless IPP --> printer
     |
     +-- message moved to Printed folder (nothing reprints on restart)
     +-- sender gets a confirmation reply

Four moving parts:

  1. An email address on your own domain. Cloudflare Email Routing is free: print@yourdomain routes to a mailbox you already have. No new account required.
  2. A mail filter that moves anything addressed to the print alias into its own IMAP folder, so the poller never touches your real inbox. This filter does more work than any of the code.
  3. The poller container: a small Python script that watches the folder over IMAP, enforces the rules, converts what needs converting, and hands the job to lp.
  4. CUPS, pointed at the printer over driverless IPP. Mine is a boring HP color laser from 2017 with a DHCP reservation. Any networked printer works; a USB printer works too if it hangs off the box running CUPS.

Full source, docker-compose.yml, and an .env.example live in the repo: github.com/jfoboston/email-to-print. If you can run one container, you can run this.

The part you should not skip

A sender allowlist that fails closed.

An open gateway means spam becomes paper

Strangers converting your toner and your paper into ads, remotely, for free. HP’s ePrint addresses are guessable enough that people have received spam printouts through the official feature. Do not run this open.

The poller checks every sender against ALLOWED_SENDERS. Not on the list? The message moves to a rejected folder and the sender gets a reply saying so. An empty allowlist means nothing prints: the system fails closed, not open. I made it work this way after about ten seconds of imagining the alternative. It was the single most-endorsed design decision in the Reddit thread, and it is the one thing to keep even if you rip out everything else.

Why not the printer’s built-in email feature?

The most common question. HP ePrint, Epson Email Print, and Brother’s equivalent all exist. Three reasons to roll your own anyway:

  • Your documents route through the vendor’s cloud. Every school form and medical document takes a round trip through HP’s servers, and the printer has to phone home constantly. This pipeline never leaves your LAN once mail hits the mailbox.
  • Zero control. The built-ins give you no real allowlist, no format conversion, no logging, and no say in failure behavior. Your own pipeline decides who prints, what converts, and what gets logged.
  • It works with any printer: including a decade-old laser that only speaks IPP. Vendor email-print dies when the vendor discontinues the service, and they have a habit of doing exactly that.

Questions from 180 actual commenters

Color vs. black-and-white, duplex, paper size?

It prints whatever you send; duplex is the CUPS queue default. For family printing the defaults are right 95% of the time: that is the feature. If you need per-job control, map subject-line keywords like [bw] or [single] to lp flags. Ten-line change.

What about duplicate prints?

Processed messages move out of the watch folder, so nothing reprints on a restart. The only way to double-print is to send twice: usually intentional. If accidental double-sends bite you, add a content hash with a short TTL: ignore identical attachments within ten minutes. Hash the source attachment, not the rendered PDF: PDF metadata changes every render, so PDF hashes never match.

Why not a WhatsApp or Signal bot?

Technically possible. But this works precisely because email requires zero new behavior. The moment you introduce “message the special bot,” you are back to the web portal nobody used. Meet people where they already are.

My mom can’t find the print button. Would this help?

This came up repeatedly, and it is where the design shines: forwarding an email is often the only reliable computer skill in the household. Two variants that work well for less technical relatives:

  • Watched folder: a “Print” folder on the family share; a script polls it every 30 seconds, sends new files to lp, and moves them to a done subfolder. Drag a PDF in, it prints. No dialogs, no drivers.
  • QR print cards: laminated cards on the fridge, one per recurring document. Scan the card, the phone opens a page with one big Print button. The QR points at a URL with a long random token that kicks a job into the same pipeline; the content regenerates server-side so the card never goes stale.

Isn’t LAN printing simpler?

If everyone lives in your house, yes: keep it; this does not replace it. The email address covers everyone outside the LAN: the kid at college, the in-laws across town, the spouse on 5G. The actual feature request was “can I send you something and have it print at the house”: and I was not going to put a VPN on my mother-in-law’s phone.

Honest warts

  • Format conversion is 95% reliable. Headless LibreOffice handles most Word docs; the occasional weird .docx comes out ugly but printable. House rule: PDF it if it matters.
  • The whole thing hangs off one mail filter that predates the project: the kind of dependency that bites in a year when you have forgotten it exists. Document yours now. (A commenter suggested moving the routing into a Cloudflare Worker that POSTs straight to the print container, eliminating the mailbox dependency. It is on the list.)
  • Proton users need Bridge. Proton does not do plain IMAP, so ProtonMail Bridge runs as a container exposing IMAP on localhost. Maintenance over several months: two bridge re-logins. Gmail or any normal IMAP provider skips this entirely.

Next endpoint on the same pipeline: a cheap thermal label printer on a second address, wired into Home Assistant, so the grocery list the family builds on the wall dashboard prints itself. That write-up lands here when it is running.