Printamos Email2Print is a self-hosted printing gateway that combines a web interface, CUPS integration, email-to-print workflows, user management, API access, and optional antivirus scanning before printing.
This application is designed for environments where you want centralized and controlled printing from a browser or from email attachments.
Typical use cases:
- upload a file in a web interface and print it to a configured CUPS printer
- send attachments by email and have them printed automatically
- control which senders are allowed to use Email2Print
- expose printing functionality through an internal API
- manage users from the web UI
- optionally scan uploaded files and email attachments with ClamAV before printing
- route printing through a separate CUPS server instead of the local container
- Web UI for file upload and printing
- Email2Print worker for processing email attachments
- CUPS integration through a configured remote or local CUPS server
- User authentication and session handling
- Existing users management page
- API page for programmatic usage
- Optional ClamAV scanning before printing
- Persistent app data and configuration directories
- PostgreSQL backend support
Before installing, make sure you have:
- Docker and Docker Compose installed
- A reachable CUPS server
- A PostgreSQL database
- Optional: a ClamAV REST API endpoint or container if you want antivirus scanning
- Optional: an IMAP/SMTP-capable mailbox for Email2Print workflows
In this example:
- the application runs on
192.168.68.xxx:8097 - the CUPS server is available at
192.168.68.yyy:631 - PostgreSQL runs in Docker on the same Compose stack
- pgAdmin runs in Docker for database administration
- ClamAV runs in Docker and exposes an HTTP scan endpoint for the app
Start from services: as requested:
services:
printamos_email2print:
container_name: printamos-mail2print
image: kosztyk/printer:postgres
restart: unless-stopped
depends_on:
- postgres
- clamav
ports:
- "192.168.68.xxx:8097:8097"
environment:
TZ: "Europe/Bucharest"
KTOR_PORT: "8097"
EXTRA_ALLOWED_ORIGINS: "http://192.168.68.xxx:8097"
# CUPS target used by the app and CUPS Admin redirect
CUPS_SERVER: "192.168.68.yyy:631"
# PostgreSQL
DB_HOST: "postgres"
DB_PORT: "5432"
DB_NAME: "printamos"
DB_USER: "printamos_user"
DB_PASSWORD: "CHANGE_ME"
# App URL and secrets
APP_BASE_URL: "http://192.168.68.xxx:8097"
APP_ENCRYPTION_KEY: "REPLACE_WITH_A_LONG_RANDOM_HEX_OR_SECRET_VALUE"
SESSION_SECRET: "REPLACE_WITH_A_LONG_RANDOM_SECRET_VALUE"
# Optional antivirus scanning
# To disable ClamAV scanning entirely, set CLAMAV_URL to an empty string
CLAMAV_URL: "http://clamav:3000/api/v1/scan"
CLAMAV_TIMEOUT_SECONDS: "30"
CLAMAV_REJECT_ON_ERROR: "true"
CLAMAV_FORM_FIELD: "FILES"
# Optional email worker settings
# ENABLE_EMAIL2PRINT: "true"
# IMAP_HOST: "mail.example.com"
# IMAP_PORT: "993"
# IMAP_USER: "printer@example.com"
# IMAP_PASSWORD: "CHANGE_ME"
# IMAP_USE_SSL: "true"
# SMTP_HOST: "mail.example.com"
# SMTP_PORT: "587"
# SMTP_USER: "printer@example.com"
# SMTP_PASSWORD: "CHANGE_ME"
volumes:
- ./cups/client.conf:/etc/cups/client.conf:ro
- ./data:/app/data
- ./config:/app/config
tmpfs:
- /run
- /tmp
postgres:
container_name: printamos-postgres
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: "printamos"
POSTGRES_USER: "printamos_user"
POSTGRES_PASSWORD: "CHANGE_ME"
TZ: "Europe/Bucharest"
volumes:
- ./postgres-data:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
pgadmin:
container_name: printamos-pgadmin
image: dpage/pgadmin4:latest
restart: unless-stopped
depends_on:
- postgres
environment:
PGADMIN_DEFAULT_EMAIL: "admin@example.com"
PGADMIN_DEFAULT_PASSWORD: "CHANGE_ME"
PGADMIN_LISTEN_PORT: "8081"
volumes:
- ./pgadmin-data:/var/lib/pgadmin
ports:
- "192.168.68.xxx:8081:8081"
clamav:
container_name: printamos-clamav
image: your-clamav-rest-image:latest
restart: unless-stopped
environment:
TZ: "Europe/Bucharest"
ports:
- "192.168.68.xxx:3000:3000"
volumes:
- ./clamav-data:/var/lib/clamavClamAV support is optional.
You can run the application in two modes:
If you do not want antivirus scanning:
- remove the
clamavservice from the Compose file - remove the
depends_onreference toclamav - set
CLAMAV_URLto an empty string, or omit allCLAMAV_*variables
Example:
CLAMAV_URL: ""In that mode:
- uploaded files are printed without antivirus scanning
- email attachments are printed without antivirus scanning
If you want malware scanning before printing:
- keep the
clamavservice - set
CLAMAV_URL - keep the
CLAMAV_*variables configured
In that mode:
- uploaded files are scanned before printing
- email attachments are scanned before printing
- infected files are rejected
- clean files are allowed to continue
The application expects an HTTP endpoint compatible with:
/api/v1/scan
and a multipart upload field named:
FILES
Because ClamAV REST containers vary, replace:
image: your-clamav-rest-image:latestwith the image you actually want to use in your environment, as long as it exposes a compatible HTTP scan API.
If your app should use a remote CUPS server, create ./cups/client.conf with content similar to:
ServerName 192.168.68.yyy:631
Encryption IfRequested
Create a working directory and subfolders:
mkdir -p printamos/cups printamos/data printamos/config printamos/postgres-data printamos/pgadmin-data printamos/clamav-data
cd printamosCreate cups/client.conf:
cat > cups/client.conf <<'EOF'
ServerName 192.168.68.yyy:631
Encryption IfRequested
EOFSave the example above as docker-compose.yml and adapt:
- IP addresses
- database credentials
- application secrets
- ClamAV container image if you want antivirus scanning
- mail settings if Email2Print is enabled
docker compose up -ddocker compose logs -fIf needed, inspect individual services:
docker compose logs -f printamos_email2print
docker compose logs -f postgres
docker compose logs -f pgadmin
docker compose logs -f clamavAfter startup, open the application in your browser:
http://192.168.68.xxx:8097
Optional:
- pgAdmin:
http://192.168.68.xxx:8081
From the main app you can:
- sign in to the app
- upload a document and print it
- open the Users page and manage users
- access the API page if enabled
- open CUPS Admin from the menu
- configure or use Email2Print if your mailbox settings are present
- Open the main web UI.
- Choose a file to upload.
- Select the target printer and print options if exposed by the UI.
- Submit the print job.
- If ClamAV scanning is enabled, the file is scanned before printing.
- If the file is clean, printing continues.
- If malware is detected, the job is rejected.
- Configure the mailbox environment variables used by the worker.
- Allow the sender address or use the application logic you configured for allowed senders.
- Send an email with one or more supported attachments.
- The worker fetches the email, scans the attachments if ClamAV is enabled, and prints only approved attachments.
When ClamAV scanning is enabled:
- uploaded files are scanned before printing
- email attachments are scanned before printing
- infected files are rejected
- clean files are allowed
- if
CLAMAV_REJECT_ON_ERROR=true, files are rejected when the antivirus service cannot be reached or returns an invalid result
Recommended settings:
CLAMAV_URL: "http://clamav:3000/api/v1/scan"
CLAMAV_TIMEOUT_SECONDS: "30"
CLAMAV_REJECT_ON_ERROR: "true"
CLAMAV_FORM_FIELD: "FILES"If you do not want ClamAV scanning:
CLAMAV_URL: ""This app is typically used with:
- a remote CUPS server
- a PostgreSQL database
- optional ClamAV REST API
- optional mail account for Email2Print
For production or shared-network use:
- replace all placeholder secrets with strong random values
- do not expose the app publicly without a reverse proxy and authentication controls
- keep database credentials out of version control
- use HTTPS when publishing the app externally
- restrict allowed senders for Email2Print
- keep
CLAMAV_REJECT_ON_ERROR=trueif you want fail-closed malware protection - review CUPS access rules on the CUPS host
Pull the latest images and recreate:
docker compose pull
docker compose up -dOr rebuild if you are using a custom local app image:
docker compose build --no-cache
docker compose up -ddocker compose logs -f printamos_email2printdocker compose logs -f postgresdocker compose logs -f pgadmindocker compose logs -f clamavcurl -F 'FILES=@/path/to/file.pdf' http://192.168.68.xxx:3000/api/v1/scanFrom the Docker host, confirm that the remote CUPS server is reachable:
curl http://192.168.68.yyy:631- CUPS Admin opens localhost
- make sure the application is configured to use
CUPS_SERVER
- make sure the application is configured to use
- all files are rejected by antivirus
- verify
CLAMAV_FORM_FIELDmatches the API expectation, for your service this should beFILES
- verify
- clean files are not printing
- check application logs and the ClamAV API response
- email attachments are not printing
- verify IMAP settings, sender restrictions, and worker logs
- the app should run without ClamAV
- set
CLAMAV_URLto an empty string and remove the optional ClamAV service
- set
This application uses and builds on code from:
- Printamos: https://github.com/PrzemyslawSwiderski/printamos
- email2Print: https://github.com/Top-Goodman/email2Print