Synopsis #
FreeBSD includes the traditional lpd spooler. CUPS is available as a package and centers its design on the Internet Printing Protocol (IPP). Either can submit reliable jobs when the printer’s connection and page-description language are understood. They differ in discovery, driver integration, administration, and client expectations.
Choose one queue owner before enabling services. Running two spoolers against the same device or publishing duplicate queues produces ambiguous failures and duplicate printer entries.
The current base lpd, lpc, and chkprintcap manual pages carry deprecation notices scheduling that facility for removal before FreeBSD 16.0. It remains available on the releases covered here, but a new installation expected to cross that release boundary should normally select CUPS or include an explicit migration plan.
Inventory the printing path #
Record four separate facts:
- Connection: USB, parallel, direct IPP, AppSocket/JetDirect, LPD, SMB, or a queue on another server.
- Printer language: driverless IPP capability, PostScript, PDF, PCL, or a model-specific raster format.
- Clients: one local FreeBSD host, several Unix-like clients, mobile devices, or a mixed network.
- Policy: local-only printing, LAN sharing, discovery, authenticated administration, and untrusted network boundaries.
Use the printer’s current technical documentation or self-test/configuration page to establish protocol and language support. A detected USB device proves transport only. It does not prove that the printer accepts the submitted document format.
For USB attachment evidence:
$ usbconfig list
# dmesg | tail
For a network printer, establish address, route, DNS, and port reachability before changing a spooler. The network-path diagnostic separates those layers.
Choose between lpd and CUPS
#
| Requirement | Prefer base lpd |
Prefer packaged CUPS |
|---|---|---|
| One known local or network queue | Suitable | Suitable |
Existing printcap(5) workflow and filters |
Native model | Requires migration into CUPS queues |
| IPP and driverless printing | Limited traditional workflow | Primary protocol and stronger fit |
| Desktop printer dialogs and mixed clients | Manual client configuration is common | Better integration and queue discovery |
| Model-specific packaged drivers | Custom filter must be designed | CUPS driver packages may integrate them |
| Minimal base-system service set | No package required | Adds packages and /usr/local configuration |
| Web administration | None | Local CUPS interface available |
Do not select a driver package solely by vendor name. Confirm that the exact model, architecture, protocol, and intended features are supported. Many modern printers work best through driverless IPP; older or host-based models may require a CUPS filter or may not be practical on FreeBSD.
Establish a narrow lpd queue
#
Use lpd when the printer language and transport are already known and a small traditional spooler is sufficient. Before editing /etc/printcap, inspect the base examples and manual:
$ less /usr/share/examples/printing/README
$ man 5 printcap
Create one queue with an explicit spool directory, device or remote queue, log path, and only the filter required for the documented printer language. Printer-specific device paths, remote queue names, and filters cannot be copied safely from a generic example.
Check the file syntax before enabling the daemon by asking chkprintcap(8) to inspect all entries:
# chkprintcap
Then enable and start the base service:
# sysrc lpd_enable=YES
# service lpd start
Submit a small known-good file in a language the printer accepts, then inspect the queue:
$ lpr -P queue-name known-good-file
$ lpq -P queue-name
Replace queue-name with the exact queue name from /etc/printcap rather than relying on an unverified default queue.
When a job queues but does not print, inspect the queue’s configured error log, device permissions, connection, and filter exit status. Do not repeatedly resubmit the same job while the cause is unknown.
Establish a local CUPS server #
Use CUPS when IPP, desktop integration, discovery, or packaged driver filters justify it. Install the package and keep administration local during initial qualification:
# pkg install cups
# sysrc cupsd_enable=YES
# service cupsd start
Open http://localhost:631/ from the same host. Add one printer using its documented IPP URI or the detected local device. Prefer a driverless queue when both printer and CUPS report compatible IPP capabilities; otherwise install only the package that explicitly supports the model.
USB printers may require a narrow devfs.rules(5) entry and a persistent devfs_system_ruleset. Derive the rule from the device that appears in dmesg and /dev, and grant it to the service’s documented group. A wildcard that changes every USB device to a permissive mode is not an acceptable printing fix. The USB-permissions guide
provides the evidence-first boundary.
Verify service and queue state from the command line:
# service cupsd status
$ lpstat -t
Use the CUPS error log under /var/log/cups/ to distinguish backend, filter, authorization, and device failures. Temporarily increasing the documented CUPS log level can collect evidence, but restore the normal level after diagnosis because job metadata and detailed requests can be sensitive.
Control discovery and network exposure #
Discovery and printing are separate. DNS-SD/mDNS can advertise or find a queue, while IPP carries administration and jobs. A manually configured IPP URI can work when discovery is intentionally disabled.
Keep CUPS bound to its package default local interfaces until sharing is required. For LAN sharing:
- define the exact client networks and interface boundary;
- expose printing separately from remote administration;
- require authentication and encryption where the client and server design supports them;
- allow the required traffic through the host firewall only after a local print succeeds;
- confirm that multicast discovery does not cross unintended networks.
Do not publish TCP port 631, traditional LPD, AppSocket, or a printer’s management interface to the public Internet as a shortcut. The safe PF change procedure preserves a rollback path when network exposure changes.
Diagnose by layer #
| Symptom | Evidence to collect |
|---|---|
| Printer absent locally | usbconfig, dmesg, device nodes, cable, and power |
| Network printer unreachable | address, route, DNS, ARP/NDP, and listening protocol |
| Queue accepts but never sends | spooler status, queue state, backend/device error, and permissions |
| Bytes arrive but output is blank or garbled | submitted format, selected driver/filter, and printer language |
| Local print works but clients cannot find it | explicit IPP URI, discovery service, firewall, and publish policy |
| Clients find duplicate queues | multiple publishers, both spoolers enabled, or printer self-advertisement |
| Some applications print and others do not | application-selected queue, toolkit backend, document format, and environment |
Retain the smallest known-good test: one host, one queue, one document, and one transport. Add discovery, additional clients, or sharing only after that path is repeatable. A maintainable desktop baseline applies the same staged approach to the rest of a graphical workstation.
Roll back cleanly #
Before changing stacks, drain or cancel understood jobs, save the existing queue configuration, and stop the old daemon. Disable only the service that is being replaced:
# sysrc lpd_enable=NO
# service lpd stop
or:
# sysrc cupsd_enable=NO
# service cupsd stop
Remove a device-permission or firewall rule only after confirming that no other service depends on it. Keep a record of the working printer URI, driver or language, package versions, and a known-good test document so a later package or firmware change can be compared.