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.
- Check the archive against
SHA256SUMS(sha256sum -c SHA256SUMS --ignore-missing,shasum -a 256 -c ...on macOS,Get-FileHashon Windows) and unpack it. - Copy the executable to a location ordinary users can't write to, such as
/opt/print-daemonorC:\Program Files\print-daemon: the service runs it as root or LocalSystem. - 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:
node dist/cli.js run # creates <home>/config.json on first start
node dist/cli.js --home ./dev-home run # use a different home directoryThen 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):
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:
{
"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:
- AWS: environment variables,
~/.aws, or an instance or task role. - Azure: the connection string, or
DefaultAzureCredential. - GCP: Application Default Credentials.
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.