DEV Community

Custodia-Admin
Custodia-Admin

Posted on Edited on Originally published at pagebolt.dev

PDF generation from HTML without managing servers

PDF generation from HTML without managing servers

Your app generates invoices. Estimates. Contracts. Shipping labels. Every document becomes a PDF.

Currently: You run wkhtmltopdf on your server. It works... until it doesn't.

Wednesday 3 PM: wkhtmltopdf process hangs on a malformed HTML file. All PDF generation freezes. Customers can't download invoices. Support tickets pile up. By 5 PM, you're SSH'ing into production servers, killing processes manually.

There's a better way. A REST API that generates PDFs from HTML. No server to manage. No hanging processes. No complexity.

The self-hosted PDF generation trap

Most developers start with wkhtmltopdf or similar CLI tools:

const { execSync } = require('child_process');
const fs = require('fs');

function generatePDF(html, filename) {
  fs.writeFileSync('/tmp/input.html', html);

  execSync(`wkhtmltopdf /tmp/input.html /tmp/${filename}.pdf`, {
    timeout: 30000
  });

  const pdf = fs.readFileSync(`/tmp/${filename}.pdf`);
  fs.unlinkSync(`/tmp/input.html`);
  fs.unlinkSync(`/tmp/${filename}.pdf`);

  return pdf;
}
Enter fullscreen mode Exit fullscreen mode

Looks simple. In production: nightmare.

Hidden costs

Process management

  • wkhtmltopdf hangs on malformed HTML → timeout → manual restart
  • No error recovery → customers stuck waiting
  • Requires /tmp cleanup (disk space issues)
  • System resource limits (ulimits) need tuning

Reliability

  • Font rendering differs by system setup
  • Timeouts are silent failures
  • Large files OOM the process
  • No graceful degradation

Scaling

  • Each PDF needs a new process spawn (~2 seconds)
  • Can't run 100 PDFs/second
  • Vertical scaling hits ceiling

Real cost

  • Server overhead: $50-100/month
  • DevOps time (process management): 3-5 hours/month
  • Support burden (timeouts, stuck downloads): high
  • Total: $500-1,000/month in effective cost

Solution: Hosted PDF API

Call an endpoint. Get a PDF back. That's it.

curl -X POST https://api.pagebolt.dev/generate_pdf \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Invoice #12345</h1><p>Total: $99.99</p>",
    "format": "A4",
    "margin": "1in"
  }'

# Response: PDF binary
Enter fullscreen mode Exit fullscreen mode

No infrastructure. No process management. No timeouts.

Before and after

Self-hosted approach

const express = require('express');
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

app.post('/invoice/:invoiceId', (req, res) => {
  try {
    const html = req.body.html;
    const filename = `invoice-${req.params.invoiceId}`;
    const tmpInput = path.join('/tmp', `${filename}.html`);
    const tmpOutput = path.join('/tmp', `${filename}.pdf`);

    fs.writeFileSync(tmpInput, html);

    // This can hang, timeout, or fail silently
    execSync(`wkhtmltopdf --margin-top 10 --margin-bottom 10 ${tmpInput} ${tmpOutput}`, {
      timeout: 30000,
      stdio: 'pipe'
    });

    const pdf = fs.readFileSync(tmpOutput);

    // Cleanup
    fs.unlinkSync(tmpInput);
    fs.unlinkSync(tmpOutput);

    res.set('Content-Type', 'application/pdf');
    res.set('Content-Disposition', `attachment; filename="${filename}.pdf"`);
    res.send(pdf);
  } catch (error) {
    console.error('PDF generation failed:', error);
    res.status(500).send('PDF generation failed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Cost: $50-100/month infrastructure + 3-5 hours DevOps = ~$700/month effective.

Hosted API approach

const axios = require('axios');

app.post('/invoice/:invoiceId', async (req, res) => {
  try {
    const response = await axios.post(
      'https://api.pagebolt.dev/generate_pdf',
      {
        html: req.body.html,
        format: 'A4',
        margin: '1in'
      },
      {
        headers: {
          'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
          'Content-Type': 'application/json'
        },
        responseType: 'arraybuffer'
      }
    );

    res.set('Content-Type', 'application/pdf');
    res.set('Content-Disposition', `attachment; filename="invoice-${req.params.invoiceId}.pdf"`);
    res.send(response.data);
  } catch (error) {
    res.status(500).send('PDF generation failed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Cost: $29/month (Starter, 5,000 PDFs/month), $0 infrastructure, $0 DevOps = $29/month total.

Real example: Invoice generation + email

E-commerce platform: generate invoice PDF, email to customer, archive to S3.

const axios = require('axios');
const nodemailer = require('nodemailer');
const AWS = require('aws-sdk');

async function generateAndEmailInvoice(order) {
  const htmlTemplate = `
    <h1>Invoice</h1>
    <p>Order ID: ${order.id}</p>
    <table>
      ${order.items.map(item => `
        <tr>
          <td>${item.name}</td>
          <td>$${item.price}</td>
        </tr>
      `).join('')}
    </table>
    <p>Total: $${order.total}</p>
  `;

  // Generate PDF
  const pdfResponse = await axios.post(
    'https://api.pagebolt.dev/generate_pdf',
    { html: htmlTemplate, format: 'A4' },
    {
      headers: { 'x-api-key': `${process.env.PAGEBOLT_API_KEY}` },
      responseType: 'arraybuffer'
    }
  );

  const pdf = pdfResponse.data;

  // Email to customer
  const mailOptions = {
    from: 'noreply@mystore.com',
    to: order.customer.email,
    subject: `Invoice for Order #${order.id}`,
    text: 'Your invoice is attached.',
    attachments: [{
      filename: `invoice-${order.id}.pdf`,
      content: pdf
    }]
  };

  await nodemailer.createTransport({...}).sendMail(mailOptions);

  // Archive to S3
  const s3 = new AWS.S3();
  await s3.putObject({
    Bucket: 'invoices-archive',
    Key: `${order.id}.pdf`,
    Body: pdf
  }).promise();

  return { success: true, invoiceId: order.id };
}
Enter fullscreen mode Exit fullscreen mode

Simple, reliable, no process management.

Comparison: wkhtmltopdf vs hosted API

Factor wkhtmltopdf Hosted API
Setup 1-2 hours 10 minutes
Infra cost $50-100/month $0
DevOps time 3-5 hours/month 0 hours
Latency 2-5 seconds 1-2 seconds
Reliability 99% (hangs, timeouts) 99.9% SLA
Scaling Capped at server Unlimited
Font support System-dependent Consistent
Custom styling Limited Full CSS3
Per-PDF cost $0.05-0.10 (infra) $0.02-0.05 (API)

When to use hosted PDF API

✅ User-facing invoice/receipt generation
✅ Automated document pipelines
✅ Report generation at scale
✅ Limited DevOps resources
✅ Unpredictable volume

Keep wkhtmltopdf if:

  • You process 10,000+ PDFs/day (break-even at scale)
  • You have dedicated DevOps
  • Documents have extreme customization

For most: hosted wins.

Getting started

  1. Sign up at pagebolt.dev (free: 100 requests/month)
  2. Get API key (1 minute)
  3. Make first PDF (5 minutes)
  4. Evaluate: Does it solve your use case?

No more hanging processes. No more 3 AM PDF generation failures.

Start free — 100 PDFs/month, no credit card.

Top comments (0)