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
Maintainers
Readme
🔤 n8n-nodes-font-installer
Install system fonts on your n8n server — directly from a workflow.
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.woff2from 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)
- Open your n8n instance
- Go to Settings → Community Nodes
- Click Install
- Enter:
n8n-nodes-font-installer - 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-installerThen 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-installerRestart 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.
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),.ttfand.otfhave the broadest compatibility..woff/.woff2work 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\Fontsand%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-customsubdirectory 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 fontconfigIf 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):
- Go to Google Cloud Console
- Create a project (or select an existing one)
- Enable the "Web Fonts Developer API"
- Go to Credentials → Create Credentials → API Key
- Copy the key
How to add it to n8n:
- In n8n: Credentials → Add Credential → Google Fonts API
- Paste your API key
- 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 nodeBuild and run:
docker build -t my-n8n .
docker run -it --rm \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
my-n8ndocker-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 nodeVolume-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:roThen 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.ttfExample 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
- Make sure Refresh Font Cache is enabled (requires
fontconfig) - Add
fontconfigto your Docker image:apk add fontconfig - 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 fontconfigGoogle 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
rootor has write permission to that path - Alternatively, the node auto-falls back to
~/.local/share/fonts/n8n-custom— checkfallbackDirectoryin 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-scriptsBuild
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 formatTest locally with n8n
# Point n8n at your local build
export N8N_CUSTOM_EXTENSIONS=/path/to/n8n-nodes-font-installer
npx n8n startProject 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:majorThe publish.ps1 script will: check your npm login → build → bump version → preview package → confirm → publish.
🤝 Contributing
Contributions, bug reports, and feature requests are welcome!
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Commit your changes:
git commit -m 'feat: add my feature' - Push to the branch:
git push origin feat/my-feature - 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.
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
