font-installer

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

Package Information

Released: 2 mo ago
Downloads: 0 weeklyΒ /Β 0 monthly
Latest Version: 0.1.6

Documentation

πŸ”€ 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

Discussion