npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

n8n-nodes-font-installer

v0.1.6

Published

n8n community node to install, list, and remove system fonts on the n8n server. Supports Google Fonts by name, direct URL download, and Base64 binary input. Solves missing-font issues in Puppeteer screenshots, HTML-to-PDF workflows, and canvas image gener

Readme

🔤 n8n-nodes-font-installer

Install system fonts on your n8n server — directly from a workflow.

npm version License: MIT n8n Community Node Node.js PayPal Donate

Solve missing-font problems in PDF generation, Puppeteer screenshots, canvas image rendering, and any other typography-dependent workflow — without touching a Dockerfile or SSH-ing into a server.

Installation · Operations · Configuration · Docker Setup · Example Workflows · Troubleshooting


🧩 What Is This?

n8n workflows that generate PDFs, screenshots (via Puppeteer), or canvas images depend on system fonts being installed on the server. The default n8n Docker image ships with almost no fonts, causing:

| Symptom | Cause | |---|---| | Text renders as □□□□□ (tofu boxes) | Font not found by the OS | | CJK characters missing | No Noto/CJK fonts installed | | Brand font doesn't appear in PDF | Custom font not on server | | Screenshot looks different from browser | System font list differs |

This node fixes all of that from inside an n8n workflow — no server access required.


✨ Features

  • 🌐 Install from Google Fonts — search by font family name, download TTF variants automatically
  • 🔗 Install from URL — download any .ttf, .otf, .woff, or .woff2 from a direct link
  • 📄 Install from Base64 — install a font from binary data piped from another node (HTTP Request, S3, etc.)
  • 📋 List Installed Fonts — see every font visible on the server
  • 🗑️ Remove Font — cleanly uninstall fonts added by this node
  • 🔄 Auto fc-cache refresh — fonts are immediately available to Puppeteer, canvas, and PDF tools
  • 🛡️ Cross-platform — Linux (Docker), macOS, and Windows all supported
  • 🔑 Google Fonts API key optional — works without a key using a public CSS API fallback
  • continueOnFail support — integrates cleanly with n8n error handling

📦 Installation

Option 1 — n8n Community Nodes UI (recommended)

  1. Open your n8n instance
  2. Go to Settings → Community Nodes
  3. Click Install
  4. Enter: n8n-nodes-font-installer
  5. Click Install and reload the page

The Font Installer node will appear in the node picker under the Transform category.

Option 2 — npm (self-hosted / custom setup)

npm install n8n-nodes-font-installer

Then point n8n at it:

# Option A — environment variable
export N8N_CUSTOM_EXTENSIONS=/path/to/node_modules/n8n-nodes-font-installer

# Option B — install directly into n8n's node_modules
cd /usr/local/lib/node_modules/n8n
npm install n8n-nodes-font-installer

Restart n8n after installation.

💙 Enjoying this node? If it saved you time, consider buying me a coffee — it helps keep the project maintained and free for everyone. Donate via PayPal

Option 3 — Docker

See Docker & Self-Hosted Setup below.


⚙️ Operations

1. Install from Google Fonts

Downloads a font family from fonts.google.com and installs it on the server.

Parameters

| Parameter | Type | Required | Default | Description | |---|---|---|---|---| | Font Family | String | ✅ | Roboto | Exact name as shown on fonts.google.com | | Variants to Install | Multi-select | ✅ | Regular (400) | Which weights/styles to download | | Install Directory | String | ❌ | (OS default) | Override the installation path | | Refresh Font Cache | Boolean | ❌ | true | Run fc-cache after install (Linux/macOS) |

Available Variants

All Available Variants · Regular (400) · Bold (700) · Italic · Bold Italic (700italic) · Thin (100) · ExtraLight (200) · Light (300) · Medium (500) · SemiBold (600) · ExtraBold (800) · Black (900)

Without API Key — the node uses the public Google Fonts CSS API (no key needed). Supports all standard variants.

With API Key — uses the Google Fonts API v1 for more reliable metadata. See Google Fonts API Credential.

Example Output

{
  "success": true,
  "fontFamily": "Roboto",
  "requestedVariants": ["regular", "700"],
  "installedFiles": ["Roboto-400.ttf", "Roboto-700.ttf"],
  "directory": "/usr/local/share/fonts/n8n-custom",
  "fallbackDirectory": false,
  "cacheRefreshed": true,
  "source": "Google Fonts CSS API (no key)"
}

2. Install from URL

Downloads a font file from any direct URL and installs it on the server.

Parameters

| Parameter | Type | Required | Default | Description | |---|---|---|---|---| | Font URL | String | ✅ | — | Direct link to a .ttf, .otf, .woff, or .woff2 file | | Save As (File Name) | String | ❌ | (from URL) | Filename to save as. Auto-detected from URL if empty. | | Install Directory | String | ❌ | (OS default) | Override the installation path | | Refresh Font Cache | Boolean | ❌ | true | Run fc-cache after install |

Supported Formats: .ttf · .otf · .woff · .woff2

Note: For system-level rendering (Puppeteer, PDF generators, node-canvas), .ttf and .otf have the broadest compatibility. .woff/.woff2 work in Puppeteer via Chromium but may not work with all font rendering libraries.

Example Output

{
  "success": true,
  "url": "https://cdn.example.com/fonts/BrandFont-Regular.ttf",
  "installedFile": "BrandFont-Regular.ttf",
  "fileSizeBytes": 142680,
  "directory": "/usr/local/share/fonts/n8n-custom",
  "fallbackDirectory": false,
  "cacheRefreshed": true
}

3. Install from Base64

Installs a font from base64-encoded binary data. Use this when the font file comes from a previous workflow step (HTTP Request node, AWS S3, Azure Blob Storage, etc.).

Parameters

| Parameter | Type | Required | Default | Description | |---|---|---|---|---| | Base64 Font Data | String (multiline) | ✅ | — | Raw base64 string or Data URI (data:font/ttf;base64,...) | | Font File Name | String | ✅ | custom-font.ttf | Filename to save as (must include extension) | | Install Directory | String | ❌ | (OS default) | Override the installation path | | Refresh Font Cache | Boolean | ❌ | true | Run fc-cache after install |

Format Detection: The node validates the decoded binary against known font magic bytes (TTF, OTF, WOFF, WOFF2) before writing. If the data doesn't look like a font, it rejects it with a clear error.

Accepts both:

  • Raw base64: AAABBAAC...
  • Data URI: data:font/ttf;base64,AAABBAAC...

Example Output

{
  "success": true,
  "installedFile": "MyBrandFont-Regular.ttf",
  "detectedFormat": "TrueType",
  "fileSizeBytes": 98432,
  "directory": "/usr/local/share/fonts/n8n-custom",
  "cacheRefreshed": true
}

4. List Installed Fonts

Returns all fonts currently installed on the server.

Parameters

| Parameter | Type | Required | Default | Description | |---|---|---|---|---| | Search Filter | String | ❌ | (all) | Filter results by family name (case-insensitive) |

How it works:

  • Linux / macOS: Uses fc-list (fontconfig) for accurate family names → falls back to directory scan if fontconfig is not installed
  • Windows: Scans C:\Windows\Fonts and %LOCALAPPDATA%\Microsoft\Windows\Fonts

Example Output

{
  "success": true,
  "platform": "linux",
  "method": "fc-list (fontconfig)",
  "totalFamilies": 47,
  "fonts": ["DejaVu Sans", "Liberation Mono", "Noto Sans", "Roboto", "..."],
  "n8nManagedFonts": {
    "directory": "/usr/local/share/fonts/n8n-custom",
    "count": 3,
    "files": ["Roboto-400.ttf", "Roboto-700.ttf", "BrandFont-Regular.ttf"]
  }
}

5. Remove Font

Removes a font that was installed by this node.

Safety: This operation only removes fonts from the n8n-custom subdirectory managed by this node. It cannot delete system fonts installed by your OS package manager.

Parameters

| Parameter | Type | Required | Description | |---|---|---|---| | Font File Name | String | ✅ | Exact filename to remove (e.g. Roboto-400.ttf). Use List Installed Fonts to find exact names. |

Example Output

{
  "success": true,
  "removedFile": "Roboto-400.ttf",
  "fileSizeBytes": 142680,
  "directory": "/usr/local/share/fonts/n8n-custom",
  "cacheRefreshed": true
}

🔧 Configuration

Font Installation Directories

The node automatically picks the best directory for your OS and user permissions:

| Platform | Primary (system-level) | Fallback (user-level) | |---|---|---| | Linux | /usr/local/share/fonts/n8n-custom | ~/.local/share/fonts/n8n-custom | | macOS | ~/Library/Fonts/n8n-custom | (same) | | Windows | %LOCALAPPDATA%\Microsoft\Windows\Fonts | (same) |

On Linux, if the system-level path is not writable (e.g. n8n running as a non-root user), the node automatically falls back to the user-level path with a note in the output.

You can always override with the Install Directory parameter.


Font Cache Refresh (fc-cache)

After installing a font on Linux/macOS, the node runs fc-cache -f to make the font immediately visible to:

  • Puppeteer / Chromium
  • node-canvas
  • PDF generation tools
  • Other processes that query fontconfig

Requirement: fontconfig must be installed on the server.

# Alpine Linux (default n8n Docker base)
apk add fontconfig

# Debian / Ubuntu
apt-get install fontconfig

If fontconfig is not installed, the node will install the font file successfully but will skip the cache refresh and include a note in the output explaining how to enable it. Fonts may not be immediately visible until the next system restart or manual fc-cache run.


Google Fonts API Credential (optional)

By default, this node works without any API key by using the public Google Fonts CSS API.

A Google Fonts API key gives you access to the official metadata API (v1) which:

  • Returns exact file URLs for all variants
  • Is more reliable for less common fonts
  • Supports direct variant enumeration

How to get an API key (free):

  1. Go to Google Cloud Console
  2. Create a project (or select an existing one)
  3. Enable the "Web Fonts Developer API"
  4. Go to Credentials → Create Credentials → API Key
  5. Copy the key

How to add it to n8n:

  1. In n8n: Credentials → Add Credential → Google Fonts API
  2. Paste your API key
  3. In the Font Installer node, the credential will be used automatically for Install from Google Fonts operations

🐳 Docker & Self-Hosted Setup

Minimal Dockerfile (recommended)

FROM n8nio/n8n:latest

USER root

# Install fontconfig so fc-cache works after font installation
RUN apk add --no-cache fontconfig && fc-cache -f

# Install this community node
RUN cd /usr/local/lib/node_modules/n8n && \
    npm install n8n-nodes-font-installer --ignore-scripts

USER node

Build and run:

docker build -t my-n8n .
docker run -it --rm \
  -p 5678:5678 \
  -v n8n_data:/home/node/.n8n \
  my-n8n

docker-compose.yml

version: '3.8'

services:
  n8n:
    build: .                       # uses the Dockerfile above
    # OR use the stock image:
    # image: n8nio/n8n:latest
    ports:
      - "5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n
      # Optional: mount a folder of pre-existing fonts into the container
      - ./my-fonts:/usr/local/share/fonts/host-fonts:ro
    environment:
      - N8N_ENCRYPTION_KEY=your-encryption-key-here
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=yourpassword
    restart: unless-stopped

volumes:
  n8n_data:

Pre-installing fonts (Alpine)

If you want specific fonts available before any workflow runs:

FROM n8nio/n8n:latest

USER root

RUN apk add --no-cache \
    fontconfig \
    ttf-freefont \
    font-noto \
    font-noto-cjk \
    font-noto-emoji \
    ttf-liberation && \
    fc-cache -fv

# Also install this community node
RUN cd /usr/local/lib/node_modules/n8n && \
    npm install n8n-nodes-font-installer --ignore-scripts

USER node

Volume-mount existing fonts

If you don't want to rebuild the Docker image, mount a local font directory:

volumes:
  - /path/to/your/fonts:/usr/local/share/fonts/mounted:ro

Then run fc-cache -f inside the container once:

docker exec -u root your-n8n-container fc-cache -f

🔁 Example Workflows

Example 1 — Install Roboto Before a Screenshot

[Manual Trigger]
      ↓
[Font Installer]
  Operation: Install from Google Fonts
  Font Family: Roboto
  Variants: Regular, Bold
  Refresh Font Cache: true
      ↓
[Puppeteer / HTML to Image Node]
  (Roboto is now available for rendering)
      ↓
[Send via Email / Slack / S3]

Example 2 — Install a Private Brand Font from a URL

[Schedule Trigger — runs on startup/deploy]
      ↓
[Font Installer]
  Operation: Install from URL
  Font URL: https://your-cdn.com/fonts/BrandFont-Regular.ttf
  Save As: BrandFont-Regular.ttf
      ↓
[IF node — check success]
  ✓ true  → [Set node: "Font ready"]
  ✗ false → [Slack: "Font install failed!"]

Example 3 — Install Font from S3 or HTTP

[HTTP Request]
  URL: https://storage.example.com/fonts/MyFont.ttf
  Response Format: File (binary)
      ↓
[Code Node]
  const base64 = $binary.data.toString('base64');
  return [{ json: { fontData: base64 } }];
      ↓
[Font Installer]
  Operation: Install from Base64
  Base64 Font Data: {{ $json.fontData }}
  Font File Name: MyFont.ttf

Example 4 — Audit Installed Fonts

[Manual Trigger]
      ↓
[Font Installer]
  Operation: List Installed Fonts
  Search Filter: (empty — list all)
      ↓
[Code Node — check if Roboto is present]
  const fonts = $json.fonts;
  const hasRoboto = fonts.includes('Roboto');
  return [{ json: { hasRoboto, totalFonts: fonts.length } }];
      ↓
[IF — hasRoboto == false]
  → [Font Installer: Install from Google Fonts]

Example 5 — Full Font Management Flow

[Webhook Trigger]  ← POST { "action": "install", "font": "Open Sans" }
      ↓
[Switch Node — action]
  "install" → [Font Installer: Install from Google Fonts]
  "list"    → [Font Installer: List Installed Fonts]
  "remove"  → [Font Installer: Remove Font]
      ↓
[Respond to Webhook]
  body: {{ $json }}

🗂️ Output Reference

All operations return a JSON object with at minimum:

| Field | Type | Description | |---|---|---| | success | boolean | Whether the operation completed without error | | directory | string | The font directory used | | fallbackDirectory | boolean | true if the user-level fallback was used instead of system-level | | directoryNote | string | Human-readable explanation of the path chosen | | cacheRefreshed | boolean | Whether fc-cache ran successfully | | cacheNote | string | Reason if fc-cache was skipped |

Install operations additionally return: | Field | Type | Description | |---|---|---| | installedFiles / installedFile | string / string[] | Filename(s) written to disk | | fileSizeBytes | number | Size of the installed font file |


🔒 Security

| Protection | Detail | |---|---| | Path traversal prevention | The Remove Font operation uses path.basename() to strip any directory components. Only files within the n8n-custom subdirectory can be removed. | | Magic-byte validation | The Base64 operation checks the first 4 bytes of the decoded data against known font signatures before writing to disk. Invalid data is rejected. | | Write-access fallback | The node never attempts to write to directories it can't access — it falls back gracefully instead of erroring. | | System font protection | Only fonts installed in the n8n-custom subdirectory can be removed. OS-managed fonts are untouched. |


🛠️ Troubleshooting

Fonts installed but not showing in Puppeteer screenshots

  1. Make sure Refresh Font Cache is enabled (requires fontconfig)
  2. Add fontconfig to your Docker image: apk add fontconfig
  3. If Puppeteer launches before the font is installed, restart the Puppeteer node or add a wait

fc-cache not found warning in output

Your server doesn't have fontconfig installed. Fonts will still be installed on disk but may not be immediately visible:

# Alpine
apk add fontconfig

# Debian/Ubuntu
apt-get install -y fontconfig

Google Font not found

  • Check the exact spelling at fonts.google.com (case-sensitive)
  • Some very new fonts may not be available via the CSS API — try adding a Google Fonts API key credential

Font installed but n8n shows "path not writable"

The node tried to write to /usr/local/share/fonts/n8n-custom but couldn't. Check:

  • n8n is running as root or has write permission to that path
  • Alternatively, the node auto-falls back to ~/.local/share/fonts/n8n-custom — check fallbackDirectory in the output

Windows — fonts installed but not visible

On Windows 10 1809+, user-installed fonts go to %LOCALAPPDATA%\Microsoft\Windows\Fonts. Applications must opt into reading user fonts. If a tool doesn't see the font, try installing to C:\Windows\Fonts by running n8n as Administrator and providing that path in Install Directory.


🧑‍💻 Development

Prerequisites

  • Node.js ≥ 18
  • npm ≥ 9

Setup

git clone https://github.com/yourusername/n8n-nodes-font-installer.git
cd n8n-nodes-font-installer
npm install --ignore-scripts

Build

npm run build        # compile TypeScript + copy SVG icons to dist/
npm run dev          # watch mode (TypeScript only)

Lint & Format

npm run lint         # ESLint check
npm run lintfix      # auto-fix ESLint issues
npm run format       # Prettier format

Test locally with n8n

# Point n8n at your local build
export N8N_CUSTOM_EXTENSIONS=/path/to/n8n-nodes-font-installer
npx n8n start

Project Structure

n8n-nodes-font-installer/
├── nodes/
│   └── FontInstaller/
│       ├── FontInstaller.node.ts       ← Main node class (all 5 operations)
│       ├── fontInstaller.svg           ← Node icon
│       ├── operations/
│       │   ├── googleFonts.ts          ← Google Fonts download logic
│       │   ├── fromUrl.ts              ← URL download logic
│       │   ├── fromBase64.ts           ← Base64 decode + install
│       │   ├── listFonts.ts            ← fc-list + directory scan
│       │   └── removeFont.ts          ← Safe font removal
│       └── utils/
│           ├── fontPaths.ts            ← OS-aware path resolution
│           └── fcCache.ts              ← fc-cache wrapper
├── credentials/
│   └── GoogleFontsApi.credentials.ts  ← Optional API key credential
├── dist/                               ← Compiled output (auto-generated)
├── scripts/
│   └── publish.ps1                    ← Local npm publish helper
├── .github/
│   └── workflows/
│       └── publish.yml                ← GitHub Actions CI/CD
├── package.json
├── tsconfig.json
└── README.md

📤 Publishing a New Version

# One-time: log in to npm
npm login

# Publish a patch release (0.1.0 → 0.1.1) — bug fixes
npm run publish:patch

# Publish a minor release (0.1.0 → 0.2.0) — new features
npm run publish:minor

# Publish a major release (0.1.0 → 1.0.0) — breaking changes
npm run publish:major

The publish.ps1 script will: check your npm login → build → bump version → preview package → confirm → publish.


🤝 Contributing

Contributions, bug reports, and feature requests are welcome!

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Commit your changes: git commit -m 'feat: add my feature'
  4. Push to the branch: git push origin feat/my-feature
  5. Open a Pull Request

Please follow the existing code style (TypeScript strict mode, no any where avoidable).


💙 Support This Project

This node is free and open-source. If it saved you time, fixed a painful Docker font problem, or just made your n8n workflows better — a small donation means a lot and helps keep the project actively maintained.

Donate via PayPal

paypal.me/donate → Font Installer

Every contribution, no matter the size, is genuinely appreciated. Thank you! 🙏


📄 License

MIT © nichham2


Made with ❤️ for the n8n community

npmjs.com/package/n8n-nodes-font-installer · Report an Issue · 💙 Donate