Print Daemon 0.1.0

Install

Print Daemon runs on Node.js, or as a self-contained executable when you receive a release archive (Install from a release archive). Install it on a machine that can reach your printers and the cloud queue, then run it as a system service so it starts at boot.

The command-line tool is called print-daemon. In these pages it is run as node dist/cli.js from the install folder; with the executable, run ./print-daemon (.\print-daemon.exe on Windows) instead.

Requirements#

All OSes Windows macOS / Linux
Runtime Node.js ≥ 20.19 (not for the executable)
ZPL / raw none none CUPS (lp) only for printers behind an OS queue
PDF / images SumatraPDF (free) CUPS
Office / text LibreOffice (free), headless
Service WinSW WinSW-x64.exe systemd / launchd (built in)

Microsoft Office is not needed. Office files are converted to PDF with headless LibreOffice. The result is very close to Word's but not pixel-identical: fonts that aren't installed get substituted, and complex layouts can shift. Install the metric-compatible fonts (Carlito, Caladea, Liberation) so line breaks match Word. If the output has to be exact, have the producer send PDF.

Driverless IPP printers and label printers on raw TCP need nothing installed on the machine.

Install from a release archive#

A release archive (print-daemon-<version>-<platform>.tar.gz, a .zip for Windows) holds one executable, print-daemon (print-daemon.exe), with Node.js, the admin UI and these pages inside it, and QUICKSTART.md, EULA.txt and NOTICE.txt. Platforms: Linux x64 and arm64 (glibc 2.28 or later), macOS arm64 and x64, Windows x64.

  1. Check the archive against SHA256SUMS (sha256sum -c SHA256SUMS --ignore-missing, shasum -a 256 -c ... on macOS, Get-FileHash on Windows) and unpack it.
  2. Copy the executable to a location ordinary users can't write to, such as /opt/print-daemon or C:\Program Files\print-daemon: the service runs it as root or LocalSystem.
  3. Use it wherever these pages say node dist/cli.js: ./print-daemon run, sudo ./print-daemon service install, and so on.

The requirements above still apply, except Node.js: LibreOffice, SumatraPDF and WinSW are separate downloads. On macOS the binary is not notarized: if a browser downloaded it, remove the quarantine flag once with xattr -dr com.apple.quarantine . in the unpacked folder. On Windows it is not code-signed yet, so SmartScreen may warn the first time.

Install the files (Node.js)#

Copy the Print Daemon folder (with its dist and node_modules folders) to a root-owned location such as /opt/print-daemon or C:\Program Files\print-daemon. The service runs that code as root or LocalSystem, so ordinary users must not be able to write to it. Run the commands below from that folder.

Run in the foreground#

Try it in the foreground first:

bash
node dist/cli.js run                     # creates <home>/config.json on first start
node dist/cli.js --home ./dev-home run   # use a different home directory

Then add a queue (Queues) and check that your printers show up (Printers). The admin UI link is printed by node dist/cli.js admin url (Admin UI & CLI).

Run as a system service#

Run these from an elevated shell (sudo on Linux/macOS, an Administrator prompt on Windows):

bash
node dist/cli.js service install --dry-run   # show what would be written and run
node dist/cli.js service install             # install, enable at boot, start
node dist/cli.js service install --winsw C:\Downloads\WinSW-x64.exe   # Windows: --winsw is required
node dist/cli.js service status | stop | start | uninstall
OS What gets installed Logs
Linux /etc/systemd/system/print-daemon.service (Restart=always); optional --user <name> journalctl -u print-daemon
macOS /Library/LaunchDaemons/com.printdaemon.service.plist (RunAtLoad, KeepAlive) /Library/Logs/PrintDaemon/print-daemon.log
Windows WinSW service print-daemon (automatic, delayed start, restart on failure). Download WinSW-x64.exe and pass --winsw <path>; it is copied to <home>\service\print-daemon.exe <home>\logs\

Use --dry-run first: it shows the unit file, plist or WinSW configuration and the commands, without changing anything.

The service records the absolute path of the node binary that ran install (or of the executable, for a release archive). If you upgrade Node.js to a different path (Homebrew paths include the version) or move the executable, run service install again.

install locks down the home directory first: chmod 700 on Linux/macOS (then chown -R to --user if given). On Windows it runs icacls <home> /inheritance:r /grant:r *S-1-5-18:(OI)(CI)F *S-1-5-32-544:(OI)(CI)F, which removes the inherited %ProgramData% permissions so only SYSTEM and Administrators can use it. After that, run CLI commands as root or from an elevated prompt.

Windows: the service runs as LocalSystem, so it doesn't see printers mapped per user. Install printers machine-wide (Settings → Printers, or Add-Printer).

Home directory#

The home directory holds the daemon's state and settings: config.json, printers.json, printed-jobs.json (dedupe), admin-token, the local dead-letter list dead-letter/, tmp/, and the installed license license (License).

OS Default
Windows %ProgramData%\PrintDaemon
macOS /Library/Application Support/PrintDaemon as root (the installed service), else ~/Library/Application Support/PrintDaemon
Linux /var/lib/print-daemon as root, else ~/.print-daemon

Override it with PRINT_DAEMON_HOME or --home. Run CLI commands with the same home as the service (for example sudo node dist/cli.js printers list on Linux and macOS); otherwise you are editing a different printers.json.

The home holds secrets and settings that name executables (sumatraPdfPath, libreOfficePath), so it must not be writable by ordinary users. See Admin UI & CLI › Security.

Configuration file#

<home>/config.json is created on the first start. ${ENV_VAR} references are expanded at startup, so secrets can stay out of the file. node dist/cli.js config validate checks the file, and config show prints it with secrets redacted. An example:

json
{
  "logLevel": "info",
  "queues": [
    {
      "type": "aws-sqs",
      "name": "us-east-warehouse-1",
      "queueUrl": "https://sqs.us-east-1.amazonaws.com/123456789012/print-jobs-wh1",
      "region": "us-east-1",
      "visibilityTimeoutSeconds": 120,
      "maxConcurrency": 4,
      "extendedPayload": { "enabled": true, "deleteAfterAck": true },
      "deadLetterQueueUrl": "https://sqs.us-east-1.amazonaws.com/123456789012/print-jobs-wh1-dlq"
    },
    {
      "type": "azure-servicebus",
      "name": "eu-warehouse-2",
      "connectionString": "${AZURE_SERVICEBUS_CONNECTION_STRING}",
      "queueName": "print-jobs-wh2"
    },
    {
      "type": "gcp-pubsub",
      "name": "gcp-warehouse-3",
      "projectId": "my-project",
      "subscription": "print-jobs-wh3-daemon"
    }
  ],
  "admin": { "enabled": true, "host": "127.0.0.1", "port": 10082 },
  "discovery": {
    "enabled": true,
    "intervalMinutes": 60,
    "autoAdd": true,
    "mdns": true,
    "osPrinters": true,
    "scan": { "enabled": true, "subnets": [], "ports": [9100], "timeoutMs": 400, "concurrency": 128 }
  },
  "printing": { "allowAdHocPrinters": true, "tcpTimeoutMs": 30000, "conversionTimeoutMs": 120000 },
  "deadLetter": { "maxAttempts": 5 },
  "dedupe": { "ttlHours": 24 }
}
Section Key settings
queues[] See Queues › Configuration
admin enabled, host (keep 127.0.0.1), port (10082)
discovery enabled, intervalMinutes, autoAdd, mdns, osPrinters, scan.{enabled,subnets,ports,timeoutMs,concurrency}
printing allowAdHocPrinters, adHocTcpPorts (default 9100–9103), tcpTimeoutMs, libreOfficePath, sumatraPdfPath, conversionTimeoutMs
deadLetter maxAttempts (default 5): delivery attempts for transient failures before a job moves to the local dead-letter list
dedupe ttlHours: how long printed job ids are remembered so redelivered messages aren't printed twice

Credentials always come from each SDK's default chain, never from code:

License#

A release comes with an evaluation (trial) license, so it prints right away. Install your own key with node dist/cli.js license install <file> or the admin UI's License tab; see License.