How to Migrate from Kaizen.io to IronPDF in C#
Migrating from Kaizen.io HTML-to-PDF to IronPDF replaces a self-hosted Docker container exposed over HTTP with an in-process .NET library. Kaizen.io HTML-to-PDF ships only as the Docker image kaizenio.azurecr.io/html-to-pdf and exposes a single REST endpoint at POST /html-to-pdf on port 8080 — there is no official .NET SDK or NuGet package, so every C# call has to be hand-rolled HttpClient against the running container. This guide walks through swapping that pattern for IronPDF's ChromePdfRenderer.
Why Migrate from Kaizen.io to IronPDF
The Container-API Challenges
Kaizen.io HTML-to-PDF is a Docker container with a deliberately small v1.x API, and the C# integration story is whatever you build yourself on top of HttpClient. That shape has real limits in production:
-
Container to Operate: You run, monitor, and update
kaizenio.azurecr.io/html-to-pdf:latestsomewhere reachable from your app — local Docker, sidecar, or a separate host. -
No .NET SDK: Every C# call is hand-rolled
HttpClientPOST + JSON. No IntelliSense, no compile-time checks on the request shape. -
Tiny v1.x API Surface: The documented JSON body accepts only an
htmlfield. URL-to-PDF, custom stylesheets, headers/footers, page size, orientation, and margins are listed as roadmap items rather than shipping features — anything beyond rendering a string of HTML must be expressed inside the HTML itself, typically via@pageCSS and absolute-positioned divs. -
No Page-Number Support: The API has no
{page}/{total}placeholders, so "Page X of Y" footers cannot be produced server-side. -
HTTP Round-Trip Per PDF: Even when the container runs on
localhost, every PDF pays JSON serialization and a socket hop. - Watermark on Free Tier: Without the
KAIZEN_PDF_LICENSEenvironment variable set on the container, output is watermarked.
Kaizen.io vs IronPDF Comparison
| Feature | Kaizen.io HTML-to-PDF | IronPDF |
|---|---|---|
| Distribution | Docker image kaizenio.azurecr.io/html-to-pdf |
NuGet IronPdf |
| C# Integration | Hand-rolled HttpClient POST (no SDK) |
Strongly-typed ChromePdfRenderer API |
| Process Model | Out-of-process container | In-process |
| Endpoint Surface (v1.x) | POST /html-to-pdf with { "html": ... } |
Methods for HTML, file, URL |
| URL Input | Roadmap | RenderUrlAsPdf(url) |
| Headers / Footers | Not in v1.x API; fake via fixed-position CSS | TextHeader/Footer and HtmlHeader/Footer |
| Page Numbers | Unsupported | {page} and {total-pages} placeholders |
| Page Size / Orientation | Embed @page CSS in HTML |
RenderingOptions.PaperSize etc. |
| Licensing | One-time license; watermarked free tier | Commercial (annual or perpetual) |
For teams standardizing on modern .NET, IronPDF removes the sidecar container and HTTP hop from the request path while exposing a strongly-typed configuration surface.
Migration Complexity Assessment
Estimated Effort by Feature
| Feature | Migration Complexity |
|---|---|
| Basic HTML to PDF | Very Low |
| HTML File to PDF | Very Low |
| URL to PDF | Low (Kaizen v1.x has no URL endpoint — workaround is replaced wholesale) |
| Headers/Footers | Medium (fake CSS divs replaced with real header zones) |
| Page Numbers | New capability (not available in Kaizen v1.x) |
| Page Settings | Low (@page CSS moves to RenderingOptions) |
Paradigm Shift
The fundamental shift is from out-of-process HTTP calls to a Docker container to in-process rendering:
Kaizen.io: HttpClient.PostAsync("http://.../html-to-pdf", { html }) → byte[]
IronPDF: ChromePdfRenderer → RenderHtmlAsPdf(html) → PdfDocument
Before You Start
Prerequisites
- .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5+
- NuGet Access: Ability to install NuGet packages
- IronPDF License: Obtain your license key from ironpdf.com
Package Changes
There is no Kaizen NuGet package to remove — Kaizen ships only as a Docker image. Stop the container and add the IronPDF package:
# Stop and remove the running Kaizen container (if any)
docker stop kaizen-pdf
docker rm kaizen-pdf
# Install IronPDF
dotnet add package IronPdf
# Stop and remove the running Kaizen container (if any)
docker stop kaizen-pdf
docker rm kaizen-pdf
# Install IronPDF
dotnet add package IronPdf
License Configuration
// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
' Add at application startup (Program.vb or Startup.vb)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
Identify Kaizen.io Usage
Because Kaizen has no SDK, calls into it look like generic HttpClient POSTs. Search for the endpoint, image name, and license env var rather than a namespace:
grep -r "html-to-pdf\|KAIZEN_PDF_LICENSE\|kaizenio.azurecr.io\|localhost:8080" \
--include="*.cs" --include="*.json" --include="*.yml" .
grep -r "html-to-pdf\|KAIZEN_PDF_LICENSE\|kaizenio.azurecr.io\|localhost:8080" \
--include="*.cs" --include="*.json" --include="*.yml" .
Complete API Reference
Concept Mappings
There is no Kaizen .NET class to map — only the JSON request shape and the conventions you built around it.
| Kaizen.io Concept | IronPDF Equivalent |
|---|---|
HttpClient + POST /html-to-pdf |
ChromePdfRenderer |
JSON { "html": "..." } body |
RenderHtmlAsPdf(string html) |
| (No URL field in v1.x) | RenderUrlAsPdf(string url) |
| (No file field in v1.x) | RenderHtmlFileAsPdf(string path) |
Inline @page CSS for size |
RenderingOptions.PaperSize |
Inline @page CSS for orientation |
RenderingOptions.PaperOrientation |
Inline @page { margin: ... } |
RenderingOptions.MarginTop/Bottom/Left/Right |
Fixed-position <div class='header'> hack |
RenderingOptions.TextHeader / HtmlHeader |
Fixed-position <div class='footer'> hack |
RenderingOptions.TextFooter / HtmlFooter |
HttpClient.PostAsync |
RenderHtmlAsPdfAsync |
Container env var KAIZEN_PDF_LICENSE |
IronPdf.License.LicenseKey |
HTTP byte[] response |
pdf.BinaryData / pdf.SaveAs(path) |
Placeholder Mappings
Kaizen v1.x has no server-side placeholders. If you previously hand-substituted strings into the HTML before POSTing, switch to IronPDF's render-time placeholders:
| What you used to do | IronPDF Placeholder |
|---|---|
html.Replace("{page}", currentPage.ToString()) |
{page} |
html.Replace("{total}", total.ToString()) |
{total-pages} |
html.Replace("{date}", DateTime.Now.ToShortDateString()) |
{date} |
html.Replace("{title}", docTitle) |
{html-title} |
Code Migration Examples
Example 1: Basic HTML to PDF
Before (Kaizen.io — POST JSON to the container):
// Container must be running:
// docker run -d -p 8080:8080 -e KAIZEN_PDF_LICENSE=... \
// kaizenio.azurecr.io/html-to-pdf:latest
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var http = new HttpClient();
var payload = JsonSerializer.Serialize(new { html = "<html><body><h1>Hello World</h1></body></html>" });
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);
response.EnsureSuccessStatusCode();
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("output.pdf", pdfBytes);
}
}
// Container must be running:
// docker run -d -p 8080:8080 -e KAIZEN_PDF_LICENSE=... \
// kaizenio.azurecr.io/html-to-pdf:latest
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var http = new HttpClient();
var payload = JsonSerializer.Serialize(new { html = "<html><body><h1>Hello World</h1></body></html>" });
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);
response.EnsureSuccessStatusCode();
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("output.pdf", pdfBytes);
}
}
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Module Program
Async Function Main() As Task
Using http As New HttpClient()
Dim payload = JsonSerializer.Serialize(New With {.html = "<html><body><h1>Hello World</h1></body></html>"})
Dim content As New StringContent(payload, Encoding.UTF8, "application/json")
Dim response = Await http.PostAsync("http://localhost:8080/html-to-pdf", content)
response.EnsureSuccessStatusCode()
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
File.WriteAllBytes("output.pdf", pdfBytes)
End Using
End Function
End Module
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System.IO;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello World</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System.IO;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello World</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Imports System.IO
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html = "<html><body><h1>Hello World</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
End Sub
End Class
The Kaizen.io approach serializes JSON, posts it to the container's REST endpoint, checks the status code, reads the response as bytes, and writes those bytes to disk. IronPDF's ChromePdfRenderer runs in-process — RenderHtmlAsPdf() returns a PdfDocument with a SaveAs() method, so the round-trip and the manual byte handling go away. See the HTML to PDF documentation for additional rendering options.
Example 2: HTML File to PDF with Page Settings
Kaizen v1.x has no file endpoint and no page-layout fields — you read the file yourself and embed page size/orientation in @page CSS.
Before (Kaizen.io):
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var body = File.ReadAllText("input.html");
var html = "<style>@page { size: A4 portrait; }</style>" + body;
using var http = new HttpClient();
var payload = JsonSerializer.Serialize(new { html });
var response = await http.PostAsync(
"http://localhost:8080/html-to-pdf",
new StringContent(payload, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
File.WriteAllBytes("document.pdf", await response.Content.ReadAsByteArrayAsync());
}
}
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var body = File.ReadAllText("input.html");
var html = "<style>@page { size: A4 portrait; }</style>" + body;
using var http = new HttpClient();
var payload = JsonSerializer.Serialize(new { html });
var response = await http.PostAsync(
"http://localhost:8080/html-to-pdf",
new StringContent(payload, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
File.WriteAllBytes("document.pdf", await response.Content.ReadAsByteArrayAsync());
}
}
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Module Program
Async Function Main() As Task
Dim body = File.ReadAllText("input.html")
Dim html = "<style>@page { size: A4 portrait; }</style>" & body
Using http As New HttpClient()
Dim payload = JsonSerializer.Serialize(New With {Key .html = html})
Dim response = Await http.PostAsync(
"http://localhost:8080/html-to-pdf",
New StringContent(payload, Encoding.UTF8, "application/json"))
response.EnsureSuccessStatusCode()
File.WriteAllBytes("document.pdf", Await response.Content.ReadAsByteArrayAsync())
End Using
End Function
End Module
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
var pdf = renderer.RenderHtmlFileAsPdf("input.html");
pdf.SaveAs("document.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
var pdf = renderer.RenderHtmlFileAsPdf("input.html");
pdf.SaveAs("document.pdf");
}
}
Imports IronPdf
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait
Dim pdf = renderer.RenderHtmlFileAsPdf("input.html")
pdf.SaveAs("document.pdf")
End Sub
End Class
RenderHtmlFileAsPdf() reads the file directly, and page size and orientation move from inline @page CSS to RenderingOptions properties.
Example 3: URL to PDF with Headers and Footers
Kaizen v1.x has no ConvertUrl, no header/footer fields, and no page-number placeholders. The workaround is to fetch the page yourself and wrap it with @page CSS and fixed-position divs to fake header/footer — with no way to render page numbers.
Before (Kaizen.io):
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var http = new HttpClient();
var page = await http.GetStringAsync("https://example.com");
var html = $@"<!doctype html><html><head><style>
@page {{ margin: 20mm; }}
.h {{ position: fixed; top: -15mm; left: 0; right: 0; text-align: center; }}
.f {{ position: fixed; bottom: -15mm; left: 0; right: 0; text-align: center; }}
</style></head><body>
<div class='h'>Company Header</div>
<div class='f'>Footer (page numbers unsupported in Kaizen v1.x)</div>
{page}
</body></html>";
var payload = JsonSerializer.Serialize(new { html });
var response = await http.PostAsync(
"http://localhost:8080/html-to-pdf",
new StringContent(payload, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
File.WriteAllBytes("webpage.pdf", await response.Content.ReadAsByteArrayAsync());
}
}
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var http = new HttpClient();
var page = await http.GetStringAsync("https://example.com");
var html = $@"<!doctype html><html><head><style>
@page {{ margin: 20mm; }}
.h {{ position: fixed; top: -15mm; left: 0; right: 0; text-align: center; }}
.f {{ position: fixed; bottom: -15mm; left: 0; right: 0; text-align: center; }}
</style></head><body>
<div class='h'>Company Header</div>
<div class='f'>Footer (page numbers unsupported in Kaizen v1.x)</div>
{page}
</body></html>";
var payload = JsonSerializer.Serialize(new { html });
var response = await http.PostAsync(
"http://localhost:8080/html-to-pdf",
new StringContent(payload, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
File.WriteAllBytes("webpage.pdf", await response.Content.ReadAsByteArrayAsync());
}
}
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Module Program
Async Function Main() As Task
Using http As New HttpClient()
Dim page As String = Await http.GetStringAsync("https://example.com")
Dim html As String = $"<!doctype html><html><head><style>
@page {{ margin: 20mm; }}
.h {{ position: fixed; top: -15mm; left: 0; right: 0; text-align: center; }}
.f {{ position: fixed; bottom: -15mm; left: 0; right: 0; text-align: center; }}
</style></head><body>
<div class='h'>Company Header</div>
<div class='f'>Footer (page numbers unsupported in Kaizen v1.x)</div>
{page}
</body></html>"
Dim payload As String = JsonSerializer.Serialize(New With {Key .html = html})
Dim response As HttpResponseMessage = Await http.PostAsync(
"http://localhost:8080/html-to-pdf",
New StringContent(payload, Encoding.UTF8, "application/json"))
response.EnsureSuccessStatusCode()
File.WriteAllBytes("webpage.pdf", Await response.Content.ReadAsByteArrayAsync())
End Using
End Function
End Module
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader.CenterText = "Company Header";
renderer.RenderingOptions.TextFooter.CenterText = "Page {page} of {total-pages}";
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader.CenterText = "Company Header";
renderer.RenderingOptions.TextFooter.CenterText = "Page {page} of {total-pages}";
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
}
}
Imports IronPdf
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.TextHeader.CenterText = "Company Header"
renderer.RenderingOptions.TextFooter.CenterText = "Page {page} of {total-pages}"
renderer.RenderingOptions.MarginTop = 20
renderer.RenderingOptions.MarginBottom = 20
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("webpage.pdf")
End Sub
End Class
RenderUrlAsPdf() fetches and renders the URL directly — no client-side page fetch — and the fake fixed-position divs are replaced with TextHeader / TextFooter zones. Page numbers are produced via the {page} and {total-pages} placeholders, which Kaizen v1.x does not provide. Learn more about URL to PDF conversion and headers and footers.
Critical Migration Notes
License Lives in Code, Not the Container
Kaizen's license is configured via the KAIZEN_PDF_LICENSE environment variable on the Docker container. IronPDF's license is a static property set once at application startup:
// DELETE the Kaizen container env var:
// docker run ... -e KAIZEN_PDF_LICENSE=... kaizenio.azurecr.io/html-to-pdf
// IronPDF: set once at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
// DELETE the Kaizen container env var:
// docker run ... -e KAIZEN_PDF_LICENSE=... kaizenio.azurecr.io/html-to-pdf
// IronPDF: set once at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
' DELETE the Kaizen container env var:
' docker run ... -e KAIZEN_PDF_LICENSE=... kaizenio.azurecr.io/html-to-pdf
' IronPDF: set once at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim renderer As New ChromePdfRenderer()
Placeholder Syntax
If you previously hand-substituted strings into the HTML before POSTing, switch to IronPDF's render-time placeholders:
{page}(unchanged){total}→{total-pages}{title}→{html-title}{date}→{date}(unchanged)
Return Type Change
Kaizen returns raw bytes over HTTP. IronPDF returns a PdfDocument:
// Kaizen.io returns byte[] via HTTP
byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("output.pdf", pdfBytes);
// IronPDF returns PdfDocument
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf"); // Direct save
byte[] bytes = pdf.BinaryData; // Or get bytes if needed
// Kaizen.io returns byte[] via HTTP
byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("output.pdf", pdfBytes);
// IronPDF returns PdfDocument
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf"); // Direct save
byte[] bytes = pdf.BinaryData; // Or get bytes if needed
Imports System.IO
' Kaizen.io returns Byte() via HTTP
Dim pdfBytes As Byte() = Await response.Content.ReadAsByteArrayAsync()
File.WriteAllBytes("output.pdf", pdfBytes)
' IronPDF returns PdfDocument
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf") ' Direct save
Dim bytes As Byte() = pdf.BinaryData ' Or get bytes if needed
Delete the HTTP Plumbing
Once the renderer is in-process, the supporting code disappears:
// DELETE all of:
// - HttpClient lifetime management
// - JSON serialization of { html }
// - response.EnsureSuccessStatusCode() / status-code branches
// - "container unreachable" retry/backoff
// - ReadAsByteArrayAsync()
// Replaced with a single in-process call:
var pdf = renderer.RenderHtmlAsPdf(html);
// DELETE all of:
// - HttpClient lifetime management
// - JSON serialization of { html }
// - response.EnsureSuccessStatusCode() / status-code branches
// - "container unreachable" retry/backoff
// - ReadAsByteArrayAsync()
// Replaced with a single in-process call:
var pdf = renderer.RenderHtmlAsPdf(html);
Troubleshooting
Issue 1: No HtmlToPdfConverter Class
Problem: There is no HtmlToPdfConverter class to swap for — Kaizen has no .NET SDK at all.
Solution: The migration is from HttpClient POSTs against the container to ChromePdfRenderer:
// Kaizen.io: hand-rolled HTTP
using var http = new HttpClient();
var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);
// IronPDF: in-process renderer
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Kaizen.io: hand-rolled HTTP
using var http = new HttpClient();
var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);
// IronPDF: in-process renderer
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
Imports System.Net.Http
Imports IronPdf
' Kaizen.io: hand-rolled HTTP
Using http As New HttpClient()
Dim response = Await http.PostAsync("http://localhost:8080/html-to-pdf", content)
End Using
' IronPDF: in-process renderer
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)
Issue 2: Where Did the Options Object Go?
Problem: Kaizen v1.x has no ConversionOptions — page layout was inline @page CSS, headers/footers were fixed-position divs.
Solution: Move that configuration to RenderingOptions on the renderer:
// Before: <style>@page { size: A4 portrait; margin: 20mm; }</style>
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
// Before: <style>@page { size: A4 portrait; margin: 20mm; }</style>
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
' Before: <style>@page { size: A4 portrait; margin: 20mm; }</style>
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait
renderer.RenderingOptions.MarginTop = 20
renderer.RenderingOptions.MarginBottom = 20
Issue 3: Page Numbers Don't Render
Problem: Kaizen v1.x has no {page} / {total} placeholder support, so any pre-migration "Page X of Y" footer was either missing or static text.
Solution: Use IronPDF's header/footer placeholders:
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
CenterText = "Page {page} of {total-pages}"
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
CenterText = "Page {page} of {total-pages}"
};
renderer.RenderingOptions.TextFooter = New TextHeaderFooter With {
.CenterText = "Page {page} of {total-pages}"
}
If you previously hand-substituted {total} in your own code before POSTing, remove that substitution — IronPDF resolves {total-pages} at render time.
Issue 4: Container Connection Errors
Problem: Code paths handling "container unreachable" / port-8080 errors.
Solution: IronPDF runs in-process — there is no endpoint to connect to. Delete the connection error handling.
Issue 5: First Render Slow
Problem: First PDF generation takes 1-3 seconds.
Solution: IronPDF initializes Chromium on first use. Warm up at application startup:
// In Program.cs or Startup.cs:
new ChromePdfRenderer().RenderHtmlAsPdf("<html></html>");
// In Program.cs or Startup.cs:
new ChromePdfRenderer().RenderHtmlAsPdf("<html></html>");
' In Program.vb or Startup.vb:
Call New ChromePdfRenderer().RenderHtmlAsPdf("<html></html>")
Migration Checklist
Pre-Migration
- Locate every Kaizen call site (search for
html-to-pdf,KAIZEN_PDF_LICENSE,kaizenio.azurecr.io,localhost:8080) - Document inline
@pageCSS used for size, orientation, margins - Document fake header/footer divs (fixed-position with negative offsets)
- List any client-side placeholder substitution (
html.Replace("{page}", ...)etc.) - Note container lifecycle: pull, run, restart, license env var
- Obtain IronPDF license key
Container Teardown
- Stop and remove the running Kaizen container (
docker stop kaizen-pdf && docker rm kaizen-pdf) - Remove Kaizen pull/run steps from CI/CD
- Remove
KAIZEN_PDF_LICENSEfrom secrets/env - Install
IronPdfNuGet package (dotnet add package IronPdf)
Code Changes
- Add license key configuration at startup
- Replace
HttpClientPOST withChromePdfRenderer - Move embedded
@pageCSS intoRenderingOptionsproperties - Replace JSON POST with
RenderHtmlAsPdf()/RenderHtmlFileAsPdf()/RenderUrlAsPdf() - Replace fake header/footer divs with
TextHeader/FooterorHtmlHeader/Footer - Update placeholder syntax (
{total}→{total-pages},{title}→{html-title}) - Replace
byte[]HTTP body withpdf.BinaryData - Use
pdf.SaveAs()instead ofFile.WriteAllBytes() - Delete container-reachability error handling and retry/backoff
Testing
- Test all PDF generation paths
- Verify header/footer rendering and page numbers (new capability)
- Validate margins, page size, and orientation
- Test offline operation (no container required)
Post-Migration
- Tear down Kaizen container infrastructure
- Update environment variables / secrets
- Remove container health checks from monitoring/alerting
- Document the new typed-exception error patterns

