All the bugs that are fit to print
My iPhone could see my home printer, but it hadn’t printed a page in 127 days. Here’s how I tracked down why, what finally fixed it, and how to check your own setup when AirPrint goes quiet.
My iPhone could see my printer but couldn’t print to it. Five problems were stacked up: a changed printer address had paused the CUPS queue, the container’s print backend crashed, the printer’s raw port needed its own driver, and two Bonjour servers on my Synology NAS, one of them in a host-networked Docker container, kept renaming each other because they published different addresses for the same name. The fix that ended it: let the NAS own its name, and stop the container’s Avahi publishing one (publish-addresses=no).
Anyone sharing a printer from a NAS or a Raspberry Pi, running Avahi in Docker, or wondering why AirPrint went quiet.
Level: intermediate. You’ll want to be comfortable in a terminal.
The sequel · No. 001 shows how my NAS gives this printer AirPrint in the first place
At 21:03 on a Monday night, my Epson L3250 printed a photo from my iPhone. An ordinary photo. It was the first page my phone had printed since the middle of June.
For 127 days, nothing had complained. The printer was on. My NAS, which relays my iPhone’s jobs to the printer because the Epson can’t do AirPrint by itself, was running. My phone could even see the printer in its print menu. Every job went in, and nothing came out.
I worked it out in one evening. It turned out to be five problems stacked on top of each other, and the one at the bottom wasn’t about printing at all. Here’s the trail, dead ends included, and what you can take from each step.
So where were my jobs going?
incidentMy printer’s address changed from .2 to .7 when the router handed out a new one. CUPS kept sending to .2, failed once, and paused the queue for good, because it was set to stop the printer on any error. Nothing told me.LessonGive printers a fixed address with a DHCP reservation, and set CUPS to retry instead of stopping.
twistContainer Manager quietly auto-updated my AirPrint container to version 2.x.LessonPin the image version of anything you depend on at home.
progressI pointed the queue at the new address and told it to retry instead of giving up.
incidentNow every job failed with “Backend failed”. A saved log named the culprit: CUPS’s ipp backend crashed with status -139, signal 11, inside the Alpine container.Lesson“Backend failed” means one of CUPS’s own helpers died. Read error_log before you blame the printer.
dead endI tried going around the crashing backend by sending raw data straight to port 9100. The printer printed pages of gibberish, and kept going.LessonPort 9100 passes bytes straight through, so the data has to be in the printer’s own language already.
progressI added Epson’s own ESC/P-R driver to the container. Now the NAS spoke the printer’s native language.
incidentMy phone could see the printer but not connect. The advert pointed at cups-airprint-3.local, a name with no address: the 2.x image runs its own Bonjour server under its own name.LessonIf a printer shows up but won’t connect, check that the host in its advert actually resolves.
dead endI pinned the image back to 1.2.0, the March version. The container’s Bonjour server started renaming itself anyway: nas-16, -50, -58.
the clueI found my NAS’s own Bonjour server running as nas-319.local. Something had made it rename itself 319 times.LessonA name ending in a number like -2 or -319 means two devices are fighting over it.
progressI restarted the NAS’s Avahi. My iPhone printed a photo.
closedI stopped the container publishing a name or addresses. Both servers settled on nas.local, with no number.
At 20:59 I stopped looking at the printer. My NAS’s process list showed its own Bonjour server, Avahi, running under the name nas-319.local. Its real name is nas.local, and nas.local no longer answered at all.
Bonjour adds -2, -3, -4 to a name when it finds someone else already using it. To reach -319, my NAS had lost an argument about its own name 319 times in the two weeks since its last reboot.
So I had a new question: who else on my network was calling themselves nas?
Wanted
for answering to a name that wasn’t theirs
“nas”
nas-2, -3, -4 … all the way to -319Reward: one printed photo
Spoiler: there was no thief. See Exhibit C.
To see what went wrong, you need one idea about home networks. Bonjour is Apple’s name for two open standards working together: mDNS for looking up names and DNS-SD for finding services. Avahi is the Linux implementation of the same two standards. My NAS runs Avahi, my AirPrint container ran a second copy, and your iPhone runs Bonjour. They all speak the same packets.
On the internet, your phone asks a DNS server for an address. At home there is no server that knows your devices, so mDNS asks everyone at once. The question goes to 224.0.0.251 on UDP port 5353, which every device on the network listens to. Whoever owns the name answers. Names ending in .local are the signal to do it this way.
DNS-SD reuses ordinary DNS record types to describe services. Your iPhone follows a chain of four lookups, each answering the next question. These are the real records on my network.
Showing up in the list isn’t enough. iOS also checks for the _universal subtype, a URF key in the TXT record, and a document format it can send. On its own, my Epson fails two of the four checks, so the iPhone leaves it out of the print menu. The NAS relay passes all four. If your printer isn’t AirPrint, this is how you give it AirPrint.
| Check the iPhone makes | Epson’s own advert | NAS advert |
|---|---|---|
| Listed under _ipp._tcp | yes | yes |
| _universal subtype | yes | yes |
| URF key in TXT | missing | URF=none |
| Accepts a format iOS sends | pwg-raster, escpr | pdf, image/urf |
| Result | Not shown as AirPrint | AirPrint printer |
With no server in charge, devices claim names themselves. Before using a name, a device probes: “does anyone own this already?” If someone answers with exactly the same records, that’s fine. If the answer is different, the device assumes a clash, adds -2 to its name, and tries again.
My AirPrint container shared the NAS’s own network (Docker’s “host networking”), so two Avahi servers ran on one machine and both claimed nas.local. The container could also see the NAS’s internal Docker networks, so its answer listed two extra addresses. Different answers for the same name meant each server treated the other as an impostor, around the clock. My NAS’s server got as far as -319. The container’s got to -67.
“Neither server was broken. Each one followed the rules exactly, and that is what kept the fight going.”What I took away from case No. 002
The fix was a rule both servers could live with. My NAS’s Avahi owns the NAS name and its addresses. The container’s Avahi stops publishing a name or addresses at all and only announces the printer. Its SRV record still points at nas.local, which the NAS answers for. If you run Avahi in a Docker container with host networking, these are the three lines to copy into the container’s avahi-daemon.conf:
[publish] publish-addresses=no publish-hinfo=no publish-workstation=no
Bonjour only gets the iPhone to the NAS. After that, printing is ordinary networking. Two of my other fixes sit on this route: the NAS sends Epson’s native ESC/P-R format to the printer’s raw port, and it no longer uses the backend that kept crashing.
Died 19 October inside an Alpine container, on every job it was given, of signal 11. It is remembered for crashing just after checking each job was valid, while its printer-monitor thread was running. It is survived by a plain socket to port 9100, which now carries every page. In lieu of flowers, please send ESC/P-R.
The photo came out at 21:03. Fifteen minutes later my NAS had its own name back: nas.local, no number. One name, one owner. And the printer, the first suspect anyone looks at, had been innocent all along.
On the print server, a queue marked disabled has given up. Turn it back on, and set it to retry so one failure can’t stop it for months.
lpstat -p cupsenable <queue> lpadmin -p <queue> -o printer-error-policy=retry-job
Give the printer a DHCP reservation in your router, and point the queue at that address.
The first command shows which host and port the printer’s advert names; the second must return an address for that host. If it doesn’t, your phone can list the printer but never reach it.
dns-sd -L "<printer name>" _ipp._tcp dns-sd -G v4 <host>.local
A name ending in -2, -3 or higher means a conflict. On a Linux box or NAS, check what Avahi is calling itself:
ps -eo args | grep "[a]vahi-daemon:"
With Docker host networking, let the host own the name. In the container’s avahi-daemon.conf, set publish-addresses=no, publish-hinfo=no and publish-workstation=no.
The raw port expects the printer’s own language. Use the manufacturer’s driver (ESC/P-R for Epson inkjets) or send the job over IPP instead.
One of CUPS’s helpers crashed. Its error_log says which one and how, so read it before blaming the printer.
Multicast does not cross routers, VLANs or subnets. A guest Wi-Fi on its own subnet will not see the printer unless the router reflects mDNS.
Devices remember adverts until their time-to-live runs out, often 75 minutes. That is why a stale “@ nas-67” lingered after the fix.
Some access points stop wireless devices talking to each other. The question never reaches the printer.
A firewall blocking UDP 5353 breaks discovery with no error. Two servers sharing that port on one machine is how this whole story started.
Each command runs until you press Ctrl-C. Work down the record chain from the top, the way Exhibit B does.
dns-sd -B _ipp._tcp
dns-sd -L "AirPrint EPSON_L3250 @ nas" _ipp._tcp
dns-sd -G v4 nas.local
dns-sd -B _services._dns-sd._udp
ps -eo args | grep "[a]vahi-daemon:"
The A record for the host named in the SRV record. The PTR, SRV and TXT records got it into the list; without an address the phone has nowhere to send the job.
nas.local. Why is that only sometimes a problem?Identical answers are allowed. It becomes a conflict when the records differ, as here, where one server also listed the Docker bridge addresses. Different answers trigger the rename rule.
Port 9100 passes bytes straight to the printer. The data was PWG raster, which this Epson only understands over IPP, not on its raw port. Sending ESC/P-R, the printer’s native format, fixed it.