Make the digital physical — Papercut brings the satisfying tactility of receipt printers to your digital life. There's something magical about physical output in our increasingly digital world. Whether it's a critical bug that needs fixing, a coffee order that just came in, or just the satisfaction of tearing off a completed task - physical receipts create a presence that pixels can't match.
- 🖨️ ESC/POS Compatible - Works with most USB receipt printers
- 🔌 Webhook-driven - Integrates with any service that supports webhooks
- 🎨 Fully Customizable - Control layout, formatting, fields, and branding
- 🐳 Docker Ready - Simple deployment with Docker Compose
- 🔧 Extensible - Easy to add new integrations and print formats
- ⚡ Real-time - Instant printing when events occur
- Receipt Printer: ESC/POS compatible USB printer
- Tested on: Epson TM-T20III
- Should work with any printer supported by python-escpos
- Cloudflare Account: For creating a tunnel to expose your webhook endpoint - Setup Guide
- Integration Source: Any webhook-capable service (Linear, Slack, etc.)
- Runtime: Docker or Python 3.14+ with uv
Note
We recommend using Cloudflare Tunnels to securely expose your papercut instance.
But you can use any other method you prefer (e.g. port-forwarding, ngrok) to expose your papercut instance.
Important
Docker on macOS cannot pass USB devices into containers.
For macOS, skip to the Manual Setup section
-
Create a
compose.yamlfilename: papercut services: papercut: image: ghcr.io/kabankz/papercut:latest volumes: - /dev/bus/usb:/dev/bus/usb:ro - ./config:/config:ro privileged: true restart: unless-stopped cloudflared: image: docker.io/cloudflare/cloudflared:2025.6.1 command: tunnel --no-autoupdate run environment: - TUNNEL_TOKEN=${TUNNEL_TOKEN} restart: unless-stopped
-
Create a
.envfileTUNNEL_TOKEN=YOUR_CLOUDFLARE_TUNNEL_TOKEN
-
Create a
config/papercut.tomlfile# Set your printer USB IDs (Linux: lsusb, macOS: ioreg) [printer] usb_vendor_id = "0x04b8" # e.g., Epson usb_product_id = "0x0e28" # model-specific profile = "TM-T20II" # Printer profile (see python-escpos documentation for available profiles) [header] company_name = "Your Company" address_line1 = "123 Main St" address_line2 = "City, State 12345" # Configure the provider you want to use [providers.linear] signing_secret = "YOUR_PROVIDER_SIGNING_SECRET"
-
Connect your webhook source
Example: Linear Integration
- Webhook URL:
https://<your-domain>/webhooks/linear - In Linear: Settings → API → Webhooks → New
- Label: "Papercut"
- URL: Your webhook URL
- Events: Select events to print (e.g., "Issue created")
- Copy the signing secret to your
papercut.toml
- Webhook URL:
-
Start papercut
[!TIP] This is what your directory structure should look like:
papercut/ ├─ compose.yaml ├─ .env └─ config/ ├─ papercut.toml └─ logo.png # Optionaldocker compose up -d
-
Clone the repository
git clone https://github.com/kabankz/papercut.git cd papercut -
Install dependencies
uv sync
-
Create a
.envfileTUNNEL_TOKEN=YOUR_CLOUDFLARE_TUNNEL_TOKEN
-
Create a
config/papercut.tomlfile# Set your printer USB IDs (Linux: lsusb, macOS: ioreg) [printer] usb_vendor_id = "0x04b8" # e.g., Epson usb_product_id = "0x0e28" # model-specific profile = "TM-T20II" # Printer profile (see python-escpos documentation for available profiles) [header] company_name = "Your Company" address_line1 = "123 Main St" address_line2 = "City, State 12345" # Configure the provider you want to use [providers.linear] signing_secret = "YOUR_PROVIDER_SIGNING_SECRET"
-
Connect your webhook source
Example: Linear Integration
- Webhook URL:
https://<your-domain>/webhooks/linear - In Linear: Settings → API → Webhooks → New
- Label: "Papercut"
- URL: Your webhook URL
- Events: Select events to print (e.g., "Issue created")
- Copy the signing secret to your
papercut.toml
- Webhook URL:
-
Start the Cloudflare Tunnel
- Remove the
papercutservice from yourcompose.yamlfile, then run:
docker compose up -d
- Remove the
-
Start papercut
make start
All configuration is managed through config/papercut.toml. Check out the default config for all available options with documentation.
Key things to configure:
- Printer IDs: Your printer's USB vendor and product IDs
- Provider settings: Webhook secrets
Tip
Place a file named logo in the config/ directory with any supported format (png, jpg, gif, bmp) to add your logo to receipts
Linux:
lsusb
# Look for your printer, e.g.:
# Bus 001 Device 004: ID 04b8:0e28 Seiko Epson Corp.
# ID ^^^^:^^^^ (vendor:product)macOS:
ioreg -p IOUSB -l | grep -E '"(idVendor|idProduct|USB Product Name)"'
# Returns values in decimal - convert to hex for configHave ideas for what to print? Found a bug? Want to add support for your favorite service? PRs welcome!
The core is intentionally generic - if it has webhooks, we can probably print it.
This project is licensed under the AGPL-3.0 License.
Made with ❤️ by KaBanks