In this guide I’m going to explain how to setup a CUPS network printing server on an ARM-Linux which will still be able to use older or discontinued printer drivers, meant for other architectures, using Docker with Qemu platform emulation. Therefore we could use for example 32bit Linux printer drivers emulated inside the container on our ARM server. In my example I’m going to setup a Dell 1250c printer, but it should work with other devices as well.

Network printing setup

For a long time I used a Dell 1250c printer, connected to a TP-Link router running OpenWRT, as a simple network printing solution at home. Using this setup, I was able to print from every machine in the local network, as long the client operating system still had support for the legacy and proprietary Dell drivers.

Soon I wanted to replace the TP-Link router with my own home-server, a small and inexpensive ARM-computer running ArchLinux. Installing and configuring a printing server using CUPS even on this platform would be easy but requires open-source printing drivers. Officially, Dell has limited driver support for Windows and MacOS only, regarding this device. An alternative driver for the Xerox Phaser 6000 from 2011, which is compatible to this Dell printer, has Linux support but is limited to the 32bit PC architecture. I won’t be able to use it on my ARM-platform.

ARM server connected to the Dell USB-printer

So somehow I have to “emulate” an 32/64bit environment with CUPS and the printing driver on the ARM board, to be able to use this printer again. I achieved this using Qemu and Docker using an experimental and lesser known container configuration.

Setting up Qemu, Docker and Cups

On the ARM board, install Docker and bootstrap an x64 ArchLinux container called archlinux-cupsd. We’ll also need the AUR package qemu-user-static-bin. The following commands were tested on ArchLinux ARM but in principle the setup should work on other distributions too.

pacman -S docker qemu-user-static-bin
gpasswd -a picloud docker
systemctl enable --now docker
DOCKER_CLI_EXPERIMENTAL=enabled docker run --privileged \
                                           -v /dev/bus/usb/:/dev/bus/usb/ \
                                           -p 631:631 \
                                           -dit \
                                           --restart always \
                                           --entrypoint /usr/bin/cupsd \
                                           --name archlinux-cupsd --platform linux/amd64 archlinux
docker exec -it archlinux-cupsd

Docker and the container will be automatically started at boot time and will forward the CUPS configuration and network printing port 631 to the host. Be sure to open this port on your firewall.

The last command will spawn a shell inside the container. We’re now going to install the CUPS daemon together with the proprietary printing drivers. Depending on the driver, you have to add custom repositories to your package manager.

[...]
[multilib]
Include = /etc/pacman.d/mirrorlist

[projectinsanity]
SigLevel = PackageOptional
Server = https://onny.project-insanity.org/archlinux
pacman -Sy cups ghostscript xerox-phaser-6000-6010
passwd

Besides installing the dependencies, we’re going to set a root password since we’ll need it later in the CUPS web admin interface.

Before starting the CUPS printing server, modify the configuration file to allow remote access. The following example is very permissive regarding network security so you might want to change this according to your needs.

[...]
Listen 0.0.0.0:631
[...]
# Restrict access to the server...
<Location />
  Order allow,deny
  Allow all
</Location>

# Restrict access to the admin pages...
<Location /admin>
  Order allow,deny
  Allow all
</Location>
[...]

Check if your USB-attached printer is recognized inside the container and check if the permissions are correct.

lsusb
[...]
# Bus 002 Device 004: ID 413c:5404 Dell 1250c Color Printer
[...]
chown root:lp /dev/bus/usb/002/004

Exit the container and if required restart it with docker start archlinux-cupsd.

Now you can setup the printer as usual via the web configuration interface at https://<YOUR_SERVER_IP>:631/admin/ (use the local ip address of your printing server). The legacy printer drivers should be also available and working.

Setting up Avahi and AirPrint

In the last step, we’ll going to configure Avahi, a daemon which will advertise our network printer to the local network. I guess CUPS would run fine together with Avahi but without Systemd-support in Docker (and no multi-platform support in Podman) it is a bit complicated to run both in the container. So I decided to run Avahi outside of the Docker container on the host system itself.

pacman -S nss-mds avahi

Create following Avahi-service file but be sure to replace following sections: name, rp, ty, adminurl and note. The part adminurl is crucial, it will correspond to the internal printer name used by CUPS. Go to https://<YOUR_SERVER_IP>:631/printers/, click on your configured printer and write down the printer name.

<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name>Dell 1250c</name>
  <service>
    <type>_ipp._tcp</type>
    <subtype>_universal._sub._ipp._tcp</subtype>
    <port>631</port>
    <txt-record>txtver=1</txt-record>
    <txt-record>qtotal=1</txt-record>
    <txt-record>rp=printers/Dell_1250c_Color_Printer</txt-record>
    <txt-record>ty=Dell 1250c</txt-record>
    <txt-record>adminurl=http://192.168.178.2:631/printers/Dell_1250c_Color_Printer</txt-record>
    <txt-record>note=Dell 1250c</txt-record>
    <txt-record>priority=0</txt-record>
    <txt-record>product=(GPL Ghostscript)</txt-record>
    <txt-record>printer-state=3</txt-record>
    <txt-record>printer-type=0x801046</txt-record>
    <txt-record>Transparent=T</txt-record>
    <txt-record>Binary=T</txt-record>
    <txt-record>Fax=F</txt-record>
    <txt-record>Color=T</txt-record>
    <txt-record>Duplex=T</txt-record>
    <txt-record>Staple=F</txt-record>
    <txt-record>Copies=T</txt-record>
    <txt-record>Collate=F</txt-record>
    <txt-record>Punch=F</txt-record>
    <txt-record>Bind=F</txt-record>
    <txt-record>Sort=F</txt-record>
    <txt-record>Scan=F</txt-record>
    <txt-record>pdl=application/octet-stream,application/pdf,application/postscript,image/jpeg,image/png,image/urf</txt-record>
    <txt-record>URF=W8,SRGB24,CP1,RS600</txt-record>
  </service>
</service-group>

Depending on your setup you should disable multicast dns in systemd-resolved since we’re going to use Avahi together with nss-mds.

[...]
MulticastDNS=no
[...]

Apply the changes.

chown avahi:avahi /etc/avahi/services/airprint.service
systemctl restart systemd-resolved # if used and required
systemctl enable --now avahi

Install network printer on other hosts

Your Linux guest system, which will use the network printer, will automatically detect the printer as soon CUPS and Avahi is running there. Use following command to see if your server is advertising the printer.

avahi-browse -art
+  wlan0 IPv4 Dell 1250c                                    _ipp._tcp            local
=  wlan0 IPv4 Dell 1250c                                    _ipp._tcp            local
   hostname = [picloud.local]
   address = [192.168.178.2]
   port = [631]
   txt = ["URF=W8,SRGB24,CP1,RS600" "pdl=application/octet-stream,application/pdf,application/postscript,image/jpeg,image/png,image/urf" "Scan=F" "Sort=F" "Bind=F" "Punch=F" "Collate=F" "Copies=T" "Staple=F" "Duplex=T" "Color=T" "Fax=F" "Binary=T" "Transparent=T" "printer-type=0x801046" "printer-state=3" "product=(GPL Ghostscript)" "priority=0" "note=Dell 1250c" "adminurl=http://192.168.178.2:631/printers/Dell_1250c_Color_Printer" "ty=Dell_1250c_Color_Printer" "rp=printers/Dell_1250c_Color_Printer" "qtotal=1" "txtver=1"]

Discovering and using the printer without installing any drivers is possible on iOS 13, Windows 10 and the lastest Ubuntu.