
How to Migrate from Gotenberg to IronPDF in C#
Migrating from Gotenberg to IronPDF transforms your .NET PDF workflow from a Docker-based microservice architecture with HTTP API calls to an in-process native C# library. This guide provides a comprehensive, step-by-step migration path that eliminates infrastructure overhead, network latency, and container management complexity for professional .NET developers.
Why Migrate from Gotenberg to IronPDF
The Gotenberg Architecture Problem
Gotenberg is a Docker-based microservice architecture for PDF generation. While powerful and flexible, it introduces significant complexity for C# applications:
- Infrastructure Overhead: Requires Docker, container orchestration (Kubernetes/Docker Compose), service discovery, and load balancing. Every deployment becomes more complex.
- Network Latency: Every PDF operation requires an HTTP call to a separate service - adding 10-100ms+ per request. This latency compounds quickly in high-volume scenarios.
- Cold Start Issues: Container startup can add 2-5 seconds to first requests. Every pod restart, every scale-up event, and every deployment triggers cold starts.
- Operational Complexity: You must manage container health, scaling, logging, and monitoring as separate concerns from your main application.
- Multipart Form Data: Every request requires constructing multipart/form-data payloads - verbose, error-prone, and tedious to maintain.
- Failure Points: Network timeouts, service unavailability, and container crashes all become your responsibility to handle.
- Version Management: Gotenberg images update separately from your application; API changes can break integrations unexpectedly.
Gotenberg vs IronPDF Comparison
| Aspect | Gotenberg | IronPDF |
|---|---|---|
| Deployment | Docker container + orchestration | Single NuGet package |
| Architecture | Microservice (REST API) | In-process library |
| Latency per request | 10-100ms+ (network round-trip) | < 1ms overhead |
| Cold start | 2-5 seconds (container init) | 1-2 seconds (first render only) |
| Infrastructure | Docker, Kubernetes, load balancers | None required |
| Failure Modes | Network, container, service failures | Standard .NET exceptions |
| API Style | REST multipart/form-data | Native C# method calls |
| Scaling | Horizontal (more containers) | Vertical (in-process) |
| Debugging | Distributed tracing needed | Standard debugger |
| Version Control | Container image tags | NuGet package versions |
For teams targeting current .NET versions, IronPDF provides a foundation with zero infrastructure dependencies that integrates natively with modern .NET patterns.
Migration Complexity Assessment
Estimated Effort by Feature
| Feature | Migration Complexity |
|---|---|
| HTML to PDF | Very Low |
| URL to PDF | Very Low |
| Custom Paper Size | Low |
| Margins | Low |
| PDF Merging | Low |
| Headers/Footers | Medium |
| Wait Delays | Low |
| PDF/A Conversion | Low |
Paradigm Shift
The fundamental shift in this Gotenberg migration is from HTTP API calls with multipart form data to native C# method calls:
Gotenberg: HTTP POST multipart/form-data to Docker container
IronPDF: Direct method calls on C# objects
Before You Start
Prerequisites
- .NET Version: IronPDF supports .NET Framework 4.6.2+ and .NET Core 3.1+ / .NET 5/6/7/8/9+
- License Key: Obtain your IronPDF license key from ironpdf.com
- Plan Infrastructure Removal: Document Gotenberg containers for decommissioning post-migration
Identify All Gotenberg Usage
# Find direct HTTP calls to Gotenberg
grep -r "gotenberg\|/forms/chromium\|/forms/libreoffice\|/forms/pdfengines" --include="*.cs" .
# Find GotenbergSharpApiClient usage
grep -r "GotenbergSharpClient\|Gotenberg.Sharp\|ChromiumRequest" --include="*.cs" .
# Find Docker/Kubernetes Gotenberg configuration
grep -r "gotenberg/gotenberg\|gotenberg:" --include="*.yml" --include="*.yaml" .
NuGet Package Changes
# Remove Gotenberg client (if using)
dotnet remove package Gotenberg.Sharp.API.Client
# Install IronPDF
dotnet add package IronPdf
Quick Start Migration
Step 1: Update License Configuration
Before (Gotenberg):
Gotenberg requires no license but requires Docker infrastructure with container URLs.
private readonly string _gotenbergUrl = "http://localhost:3000";Private ReadOnly _gotenbergUrl As String = "http://localhost:3000"Private ReadOnly _gotenbergUrl As String = "http://localhost:3000"After (IronPDF):
// Set once at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";// Set once at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";netnetStep 2: Update Namespace Imports
// Before (Gotenberg)
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;// Before (Gotenberg)
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Imports IronPdf
Imports IronPdf.RenderingImports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Imports IronPdf
Imports IronPdf.RenderingComplete API Reference
Gotenberg Endpoint to IronPDF Mapping
| Gotenberg Route | IronPDF Equivalent |
|---|---|
POST /forms/chromium/convert/html | ChromePdfRenderer.RenderHtmlAsPdf() |
POST /forms/chromium/convert/url | ChromePdfRenderer.RenderUrlAsPdf() |
POST /forms/pdfengines/merge | PdfDocument.Merge() |
POST /forms/pdfengines/convert | pdf.SaveAs() with settings |
GET /health | N/A |
Form Parameter to RenderingOptions Mapping
| Gotenberg Parameter | IronPDF Property | Conversion Notes |
|---|---|---|
paperWidth (inches) | RenderingOptions.PaperSize | Use enum or custom size |
paperHeight (inches) | RenderingOptions.PaperSize | Use enum or custom size |
marginTop (inches) | RenderingOptions.MarginTop | Multiply by 25.4 for mm |
marginBottom (inches) | RenderingOptions.MarginBottom | Multiply by 25.4 for mm |
printBackground | RenderingOptions.PrintHtmlBackgrounds | Boolean |
landscape | RenderingOptions.PaperOrientation | Landscape enum |
waitDelay (e.g. "3s") | RenderingOptions.WaitFor.RenderDelay(ms) | Convert string to milliseconds |
Code Migration Examples
Example 1: Basic HTML to PDF
Before (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergExample
Sub Main()
MainAsync().GetAwaiter().GetResult()
End Sub
Private Async Function MainAsync() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Hello from Gotenberg</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("output.pdf", pdfBytes)
Console.WriteLine("PDF generated successfully")
End Using
End Using
End Function
End ModuleImports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergExample
Sub Main()
MainAsync().GetAwaiter().GetResult()
End Sub
Private Async Function MainAsync() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Hello from Gotenberg</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("output.pdf", pdfBytes)
Console.WriteLine("PDF generated successfully")
End Using
End Using
End Function
End ModuleAfter (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Module IronPdfExample
Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html = "<html><body><h1>Hello from IronPDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully")
End Sub
End Module' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Module IronPdfExample
Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html = "<html><body><h1>Hello from IronPDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully")
End Sub
End ModuleThe difference is substantial: Gotenberg requires constructing an HttpClient, building MultipartFormDataContent, making an async HTTP POST to a running Docker container, and handling the byte array response. IronPDF reduces this to three lines with a ChromePdfRenderer method call - no network overhead, no container dependency, no async complexity. See the HTML to PDF documentation for additional rendering options.
Example 2: URL to PDF Conversion
Before (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergUrlToPdf
Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
content.Add(New StringContent("https://example.com"), "url")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("webpage.pdf", pdfBytes)
Console.WriteLine("PDF from URL generated successfully")
End Using
End Using
End Function
End ModuleImports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergUrlToPdf
Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
content.Add(New StringContent("https://example.com"), "url")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("webpage.pdf", pdfBytes)
Console.WriteLine("PDF from URL generated successfully")
End Using
End Using
End Function
End ModuleAfter (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Class IronPdfUrlToPdf
Shared Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("PDF from URL generated successfully")
End Sub
End Class' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Class IronPdfUrlToPdf
Shared Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("PDF from URL generated successfully")
End Sub
End ClassThe Gotenberg approach requires a different endpoint (/forms/chromium/convert/url), building multipart content with the URL as a form field, and handling async HTTP responses. IronPDF's RenderUrlAsPdf() method accepts the URL directly and returns a PdfDocument object synchronously. Learn more about URL to PDF conversion.
Example 3: Custom Paper Size and Margins
Before (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Class GotenbergCustomSize
Shared Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
content.Add(New StringContent("8.5"), "paperWidth")
content.Add(New StringContent("11"), "paperHeight")
content.Add(New StringContent("0.5"), "marginTop")
content.Add(New StringContent("0.5"), "marginBottom")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes)
Console.WriteLine("Custom size PDF generated successfully")
End Using
End Using
End Function
End ClassImports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Class GotenbergCustomSize
Shared Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
content.Add(New StringContent("8.5"), "paperWidth")
content.Add(New StringContent("11"), "paperHeight")
content.Add(New StringContent("0.5"), "marginTop")
content.Add(New StringContent("0.5"), "marginBottom")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes)
Console.WriteLine("Custom size PDF generated successfully")
End Using
End Using
End Function
End ClassAfter (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Rendering
Class IronPdfCustomSize
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 50
renderer.RenderingOptions.MarginBottom = 50
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("custom-size.pdf")
Console.WriteLine("Custom size PDF generated successfully")
End Sub
End Class' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Rendering
Class IronPdfCustomSize
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 50
renderer.RenderingOptions.MarginBottom = 50
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("custom-size.pdf")
Console.WriteLine("Custom size PDF generated successfully")
End Sub
End ClassGotenberg requires string-based parameters ("8.5", "11", "0.5") added to multipart form data - no type safety, no IntelliSense, easy to mistype. IronPDF provides strongly-typed properties with PdfPaperSize enums and numeric margin values. Note that IronPDF margins are in millimeters (50mm ≈ 2 inches), while Gotenberg uses inches.
Critical Migration Notes
Unit Conversions
The most important conversion in this Gotenberg migration is the margin units:
// Gotenberg: margins in inches
content.Add(new StringContent("0.5"), "marginTop"); // 0.5 inches
content.Add(new StringContent("1"), "marginBottom"); // 1 inch
// IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7; // 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4; // 1 inch × 25.4 = 25.4mm// Gotenberg: margins in inches
content.Add(new StringContent("0.5"), "marginTop"); // 0.5 inches
content.Add(new StringContent("1"), "marginBottom"); // 1 inch
// IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7; // 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4; // 1 inch × 25.4 = 25.4mm' Gotenberg: margins in inches
content.Add(New StringContent("0.5"), "marginTop") ' 0.5 inches
content.Add(New StringContent("1"), "marginBottom") ' 1 inch
' IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7 ' 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4 ' 1 inch × 25.4 = 25.4mm' Gotenberg: margins in inches
content.Add(New StringContent("0.5"), "marginTop") ' 0.5 inches
content.Add(New StringContent("1"), "marginBottom") ' 1 inch
' IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7 ' 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4 ' 1 inch × 25.4 = 25.4mmConversion formula: millimeters = inches × 25.4
Synchronous vs Asynchronous
Gotenberg requires async operations because of HTTP communication:
// Gotenberg: Forced async due to network calls
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
// IronPDF: Synchronous in-process execution
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// IronPDF: Async wrapper if needed
var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(html));// Gotenberg: Forced async due to network calls
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
// IronPDF: Synchronous in-process execution
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// IronPDF: Async wrapper if needed
var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(html));Imports System.Net.Http
' Gotenberg: Forced async due to network calls
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
' IronPDF: Synchronous in-process execution
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
' IronPDF: Async wrapper if needed
Dim pdf = Await Task.Run(Function() renderer.RenderHtmlAsPdf(html))Imports System.Net.Http
' Gotenberg: Forced async due to network calls
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
' IronPDF: Synchronous in-process execution
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
' IronPDF: Async wrapper if needed
Dim pdf = Await Task.Run(Function() renderer.RenderHtmlAsPdf(html))Error Handling
// Gotenberg: HTTP error handling
try
{
var response = await client.PostAsync(gotenbergUrl, content);
response.EnsureSuccessStatusCode(); // What if 500? 503? Timeout?
}
catch (HttpRequestException ex) { /* Network error */ }
catch (TaskCanceledException ex) { /* Timeout */ }
// IronPDF: Standard .NET exceptions
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
}
catch (Exception ex)
{
Console.WriteLine($"PDF generation failed: {ex.Message}");
}// Gotenberg: HTTP error handling
try
{
var response = await client.PostAsync(gotenbergUrl, content);
response.EnsureSuccessStatusCode(); // What if 500? 503? Timeout?
}
catch (HttpRequestException ex) { /* Network error */ }
catch (TaskCanceledException ex) { /* Timeout */ }
// IronPDF: Standard .NET exceptions
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
}
catch (Exception ex)
{
Console.WriteLine($"PDF generation failed: {ex.Message}");
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
' Gotenberg: HTTP error handling
Try
Dim response = Await client.PostAsync(gotenbergUrl, content)
response.EnsureSuccessStatusCode() ' What if 500? 503? Timeout?
Catch ex As HttpRequestException
' Network error
Catch ex As TaskCanceledException
' Timeout
End Try
' IronPDF: Standard .NET exceptions
Try
Dim pdf = renderer.RenderHtmlAsPdf(html)
Catch ex As Exception
Console.WriteLine($"PDF generation failed: {ex.Message}")
End TryImports System
Imports System.Net.Http
Imports System.Threading.Tasks
' Gotenberg: HTTP error handling
Try
Dim response = Await client.PostAsync(gotenbergUrl, content)
response.EnsureSuccessStatusCode() ' What if 500? 503? Timeout?
Catch ex As HttpRequestException
' Network error
Catch ex As TaskCanceledException
' Timeout
End Try
' IronPDF: Standard .NET exceptions
Try
Dim pdf = renderer.RenderHtmlAsPdf(html)
Catch ex As Exception
Console.WriteLine($"PDF generation failed: {ex.Message}")
End TryInfrastructure Removal
After migration, remove Gotenberg from your infrastructure:
# REMOVE from docker-compose.yml:
# services:
# gotenberg:
# image: gotenberg/gotenberg:8
# ports:
# - "3000:3000"
# deploy:
# resources:
# limits:
# memory: 2G
Performance Considerations
Latency Comparison
| Operation | Gotenberg (Warm) | Gotenberg (Cold Start) | IronPDF (First Render) | IronPDF (Subsequent) |
|---|---|---|---|---|
| Simple HTML | 150-300ms | 2-5 seconds | 1-2 seconds | 50-150ms |
| Complex HTML | 500-1500ms | 3-7 seconds | 1.5-3 seconds | 200-800ms |
| URL Render | 1-5 seconds | 3-10 seconds | 1-5 seconds | 500ms-3s |
Infrastructure Cost Elimination
| Resource | Gotenberg | IronPDF |
|---|---|---|
| Containers required | 1-N (scaling) | 0 |
| Memory per container | 512MB-2GB | N/A |
| Network overhead per request | 10-100ms | 0ms |
| Health check endpoints | Required | Not needed |
| Load balancer | Often needed | Not needed |
Troubleshooting
Issue 1: HttpClient Patterns Not Needed
Problem: Code still using HttpClient and MultipartFormDataContent.
Solution: Replace entirely with ChromePdfRenderer:
// Remove all of this:
// using var client = new HttpClient();
// using var content = new MultipartFormDataContent();
// content.Add(new StringContent(html), "files", "index.html");
// var response = await client.PostAsync(url, content);
// Replace with:
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);// Remove all of this:
// using var client = new HttpClient();
// using var content = new MultipartFormDataContent();
// content.Add(new StringContent(html), "files", "index.html");
// var response = await client.PostAsync(url, content);
// Replace with:
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)Issue 2: Margin Units Wrong
Problem: PDFs have incorrect margins after migration.
Solution: Convert inches to millimeters:
// Gotenberg used inches: "0.5"
// IronPDF uses millimeters: 0.5 × 25.4 = 12.7
renderer.RenderingOptions.MarginTop = 12.7;// Gotenberg used inches: "0.5"
// IronPDF uses millimeters: 0.5 × 25.4 = 12.7
renderer.RenderingOptions.MarginTop = 12.7;netnetIssue 3: Container URL References
Problem: Code contains http://gotenberg:3000 or similar URLs.
Solution: Remove all container URL references - IronPDF runs in-process:
// Remove:
// private readonly string _gotenbergUrl = "http://gotenberg:3000";
// IronPDF needs no URL - it's in-process
var renderer = new ChromePdfRenderer();// Remove:
// private readonly string _gotenbergUrl = "http://gotenberg:3000";
// IronPDF needs no URL - it's in-process
var renderer = new ChromePdfRenderer();' Remove:
' Private ReadOnly _gotenbergUrl As String = "http://gotenberg:3000"
' IronPDF needs no URL - it's in-process
Dim renderer As New ChromePdfRenderer()' Remove:
' Private ReadOnly _gotenbergUrl As String = "http://gotenberg:3000"
' IronPDF needs no URL - it's in-process
Dim renderer As New ChromePdfRenderer()Migration Checklist
Pre-Migration
- Inventory all Gotenberg HTTP calls in codebase
- Document current Gotenberg configuration (timeouts, margins, paper sizes)
- Identify all Docker/Kubernetes Gotenberg configuration
- Obtain IronPDF license key
- Plan infrastructure decommissioning
Code Migration
- Install IronPDF NuGet package:
dotnet add package IronPdf - Remove Gotenberg client packages
- Replace all HTTP calls to Gotenberg with IronPDF method calls
- Convert margin units from inches to millimeters
- Update error handling (HTTP errors → .NET exceptions)
- Add license key initialization at startup
Infrastructure Migration
- Remove Gotenberg from Docker Compose / Kubernetes
- Update CI/CD pipelines (remove Gotenberg image pulls)
- Remove Gotenberg health checks
- Remove Gotenberg URL from configuration
Testing
- Test HTML to PDF conversion
- Test URL to PDF conversion
- Verify margin and sizing accuracy
- Performance test under load
- Test first-render warmup time
Post-Migration
- Remove Gotenberg container deployments
- Archive Gotenberg configuration files
- Update documentation
- Monitor application memory usage
- Verify no orphaned network connections
Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.
Related Articles


