Moves the completed change to openspec/changes/archive/2026-10-18-add-cups-printserver-container/ and syncs the printserver/cups capability into openspec/specs/.
Printserver
A containerized CUPS print server that shares a Brother HL-4150CDN network printer over IPP and advertises it via AirPrint (mDNS/DNS-SD). It prints by sending PostScript (BR-Script3) straight to the printer, so no vendor driver and no 32-bit libraries run in the container.
The container bundles:
- CUPS (IPP + web admin on port 631) with cups-filters for format conversion
- Avahi / DNS-SD for AirPrint discovery (UDP 5353)
- Brother's official HL-4150CDN BR-Script3 PPD for device options
How it works
The HL-4150CDN emulates PCL6 and BR-Script3 (PostScript 3), and accepts
PostScript over raw AppSocket (port 9100). CUPS converts whatever a client
sends into PostScript and the BR-Script3 PPD turns the selected queue options
into PostScript setpagedevice instructions:
macOS / iOS / Windows / Linux container printer
┌─────────────────┐ IPP/AirPrint ┌──────────────────────────┐ raw ┌────────────┐
│ AirPrint (URF) │ ───────────────▶ │ CUPS │ ──────▶ │ HL-4150CDN │
│ PDF / PostScript│ │ URF/PWG/PDF → PostScript│ :9100 │ BR-Script3 │
└─────────────────┘ │ pstops → setpagedevice │ └────────────┘
└──────────────────────────┘
For example, Duplex=DuplexNoTumble BRPrintQuality=Color InputSlot=Tray2
becomes:
<</Duplex true /Tumble false>>setpagedevice
<</BRProcessColor 4 /BRHrc 0>>setpagedevice
<</ManualFeed false /BRTraysw false /BRFeeder 1>> setpagedevice
Layout
| Path | Purpose |
|---|---|
Containerfile |
Image definition (Debian base, CUPS + cups-filters + Avahi, BR-Script3 PPD) |
cupsd.conf |
Container CUPS configuration (listen on all interfaces, share printers) |
entrypoint.sh |
Starts D-Bus, Avahi and CUPS, then registers the printer queue |
assets/Brother HL-4150CDN/ppd/ |
Official BR-Script3 PPD used for the queue |
assets/Brother HL-4150CDN/Windows/ |
Vendor Windows driver the PPD was extracted from |
Prerequisites
- Podman (or a compatible builder such as Docker/Buildah)
- The printer reachable on the network, with a known IP address or hostname
Build
Run from the repository root:
podman build -t printserver:latest .
The image is architecture-independent (no i386 or other 32-bit binaries), so it builds natively on x86_64 and arm64 hosts alike.
Run
mDNS does not work through a normal container network, so use host networking:
podman run -d --name printserver \
--network host \
-e PRINTER_IP=192.168.100.7 \
-e PRINTER_NAME=HL-4150CDN \
printserver:latest
Environment variables
| Variable | Default | Description |
|---|---|---|
PRINTER_IP |
(empty) | IP address / host name of the network printer. A device URI is derived from it (see PRINTER_PROTOCOL). |
PRINTER_URI |
(empty) | Full device URI. Overrides PRINTER_IP when set. |
PRINTER_PROTOCOL |
socket |
Protocol used with PRINTER_IP: socket, lpd or ipp. |
PRINTER_NAME |
HL4150CDN |
CUPS queue name. Also used as the name advertised via AirPrint/DNS-SD. |
PRINTER_DESCRIPTION |
(empty) | CUPS printer description (lpadmin -D), shown in the CUPS web UI. |
PPD_FILE |
/usr/share/cups/model/BR4150_2.PPD |
PPD used for the queue. |
No queue is created when neither PRINTER_IP nor PRINTER_URI is set.
Choosing the connection
The HL-4150CDN accepts PostScript directly, so the default is raw AppSocket.
Set PRINTER_IP and let the entrypoint build the URI:
PRINTER_PROTOCOL |
Resulting URI |
|---|---|
socket (default) |
socket://<ip>:9100 — raw AppSocket/JetDirect |
lpd |
lpd://<ip>/binary_p1 — LPR/LPD |
ipp |
ipp://<ip>/ipp/print |
For example, a printer at 192.168.100.7:
-e PRINTER_IP=192.168.100.7
Or specify the URI explicitly for full control:
-e PRINTER_URI=socket://192.168.100.7:9100
Advertised name
PRINTER_NAME is used as the CUPS queue name and as the name clients see in
AirPrint discovery. To show a descriptive label in the CUPS web interface,
set PRINTER_DESCRIPTION (this does not change the advertised name):
-e PRINTER_NAME=HL4150CDN -e PRINTER_DESCRIPTION="Brother HL-4150CDN (Office)"
Printing options
The BR-Script3 PPD exposes the HL-4150CDN's hardware options. Common ones:
| Option | PPD keyword | Values |
|---|---|---|
| Paper source | InputSlot |
AutoSelect, Tray1, Tray2, MPTray |
| Two-sided printing | Duplex |
None, DuplexNoTumble (long edge), DuplexTumble (short edge) |
| Colour / mono | BRPrintQuality |
Auto, Color, Black |
| Colour mode | BRColorMode |
Normal, Vivid, None |
| Media type | BRMediaType |
Plain, Thin, Thick, Recycled, Glossy, Label, envelopes, … |
| Print quality | CAPT |
Fine (600×600), SuperFine (2400 dpi class) |
| Toner save | TonerSaveMode |
True, False |
| Secure print | BRJobHold |
None, Private (+ BRJobPIN) |
They can be selected per job, e.g.:
podman exec printserver lp -d HL4150CDN \
-o Duplex=DuplexNoTumble -o BRPrintQuality=Color -o InputSlot=Tray1 report.pdf
Client support
| Platform | Path | Notes |
|---|---|---|
| macOS | AirPrint / IPP Everywhere | Discovered via mDNS; no driver needed |
| iOS / iPadOS | AirPrint | Discovered via mDNS; no driver needed |
| Linux | IPP / IPP Everywhere | Native or with the shared PPD |
| Windows | IPP or raw 9100 | Install Brother's BR-Script3 Windows driver, pointed at the printer or the shared queue |
Windows uses a client-side Brother driver; the server does not share via SMB.
Verify
Browse the CUPS web interface:
open http://localhost:631/ # printers
open http://localhost:631/admin # administration
From the host, or inside the container:
podman exec printserver lpstat -t # queues and status
podman exec printserver lpstat -v HL4150CDN # device URI
podman exec printserver lpoptions -p HL4150CDN -l # available options
podman exec printserver avahi-browse -rt _ipp._tcp # mDNS advertisement
A print job can be submitted with:
podman exec printserver lp -d HL4150CDN /etc/hostname
On macOS/iOS the shared queue should appear in the printer list via AirPrint.
Notes and limitations
-
Host networking is required for Avahi to send and receive mDNS traffic.
-p 631:631alone is enough for direct IPP access but not for AirPrint discovery. -
Printer state is not persisted. The queue is recreated on every start from
PRINTER_URI. To keep CUPS configuration across container replacement, mount a volume over/etc/cups:-v printserver-cups:/etc/cups -
Colour handling is done by the printer's own PostScript RIP. Output may differ slightly from Brother's host-side (rasterising) driver.
-
The queue advertises AirPrint capabilities (
image/urf,image/pwg-raster,sides-supported,color-supported). macOS and iOS printing is converted by cups-filters; verify end to end on your network.