<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ivan Muñoz</title>
    <description>The latest articles on Forem by Ivan Muñoz (@ivanalemunioz).</description>
    <link>https://forem.com/ivanalemunioz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2343624%2Fee5a004b-b03c-4775-9531-3c4a14dc296d.jpg</url>
      <title>Forem: Ivan Muñoz</title>
      <link>https://forem.com/ivanalemunioz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ivanalemunioz"/>
    <language>en</language>
    <item>
      <title>Deploying an HTML‑to‑PDF API on Vercel with Puppeteer</title>
      <dc:creator>Ivan Muñoz</dc:creator>
      <pubDate>Tue, 12 Aug 2025 12:29:19 +0000</pubDate>
      <link>https://forem.com/ivanalemunioz/deploying-an-html-to-pdf-api-on-vercel-with-puppeteer-2jp9</link>
      <guid>https://forem.com/ivanalemunioz/deploying-an-html-to-pdf-api-on-vercel-with-puppeteer-2jp9</guid>
      <description>&lt;p&gt;&lt;em&gt;Original article &lt;a href="https://buglesstack.com/blog/deploying-an-htmltopdf-api-on-vercel-with-puppeteer/" rel="noopener noreferrer"&gt;https://buglesstack.com/blog/deploying-an-html-to-pdf-api-on-vercel-with-puppeteer/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In this article, we will explore how to create an HTML-to-PDF API on &lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; using &lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/ivanalemunioz/html-to-pdf-on-vercel" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fivanalemunioz%2Fhtml-to-pdf-on-vercel&amp;amp;demo-title=HTML%E2%80%91to%E2%80%91PDF%20API%20with%20Puppeteer%20&amp;amp;demo-description=Template%20to%20deploy%20an%20HTML%E2%80%91to%E2%80%91PDF%20API%20on%20Vercel%20using%20Puppeteer.&amp;amp;demo-url=https%3A%2F%2Fhtml-to-pdf-on-vercel.vercel.app%2F&amp;amp;demo-image=https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F876c502b-f015-414b-aa4c-81b222330300" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvercel.com%2Fbutton" alt="Deploy with Vercel" width="103" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://html-to-pdf-on-vercel.vercel.app/" rel="noopener noreferrer"&gt;https://html-to-pdf-on-vercel.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Project Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest html-to-pdf-on-vercel &lt;span class="nt"&gt;--typescript&lt;/span&gt; &lt;span class="nt"&gt;--tailwind&lt;/span&gt; &lt;span class="nt"&gt;--app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, install the packages &lt;code&gt;puppeteer-core @sparticuz/chromium&lt;/code&gt; for running Puppeteer in Vercel and &lt;code&gt;puppeteer&lt;/code&gt; for local development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;puppeteer-core @sparticuz/chromium
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; puppeteer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Setup the HTML to PDF api route
&lt;/h2&gt;

&lt;p&gt;Create a new file at &lt;code&gt;app/api/pdf/route.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlParam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;htmlParam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide the HTML.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isVercel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERCEL_ENV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isVercel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pptr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isVercel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defaultArgs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlParam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;printBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline; filename="page-output.pdf"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An error occurred while generating the PDF.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route handles and HTML as a url query param and add it to the page with &lt;code&gt;page.setContent()&lt;/code&gt; to then generate the PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Add a frontend to call the API
&lt;/h2&gt;

&lt;p&gt;To interact with our API, let's create a simple frontend. Replace the content of &lt;code&gt;app/page.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p style="text-align:center"&amp;gt;
  Hello World! 
  &amp;lt;br /&amp;gt;
  &amp;lt;b&amp;gt;
    This PDF was created using 
    &amp;lt;br /&amp;gt;
    &amp;lt;a href="https://github.com/ivanalemunioz/html-to-pdf-on-vercel"&amp;gt;
      https://github.com/ivanalemunioz/html-to-pdf-on-vercel
    &amp;lt;/a&amp;gt;
  &amp;lt;/b&amp;gt;
&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHtml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultHtml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createPDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter a valid HTML.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`/api/pdf?html=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to create PDF.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;objectUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Desired filename&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Temporarily add to the DOM&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Programmatically click the link to trigger download&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Remove the link&lt;/span&gt;
      &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;objectUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Release the object URL&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An unknown error occurred.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full max-w-2xl text-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl font-bold mb-4 text-gray-800"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          HTML to PDF on Vercel using Puppeteer
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-lg text-gray-600 mb-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Enter the HTML below to generate a PDF using Puppeteer running in
          a Vercel Function.
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-2 flex-col"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;textarea&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://vercel.com"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-grow p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 text-black focus:outline-none font-mono"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
            &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createPDF&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 disabled:bg-gray-400 transition-colors"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Creating PDF...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create PDF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-red-500 mt-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Vercel Configuration
&lt;/h2&gt;

&lt;p&gt;To ensure Puppeteer runs correctly when deployed, you need to configure Next.js.&lt;/p&gt;

&lt;p&gt;Update your &lt;code&gt;next.config.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* config options here */&lt;/span&gt;
  &lt;span class="c1"&gt;// The `serverExternalPackages` option allows you to opt-out of bundling dependencies in your Server Components.&lt;/span&gt;
  &lt;span class="na"&gt;serverExternalPackages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;puppeteer-core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Try it
&lt;/h2&gt;

&lt;p&gt;Run the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; with your browser to try it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Deploy it to Vercel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fivanalemunioz%2Fhtml-to-pdf-on-vercel&amp;amp;demo-title=HTML%E2%80%91to%E2%80%91PDF%20API%20with%20Puppeteer%20&amp;amp;demo-description=Template%20to%20deploy%20an%20HTML%E2%80%91to%E2%80%91PDF%20API%20on%20Vercel%20using%20Puppeteer.&amp;amp;demo-url=https%3A%2F%2Fhtml-to-pdf-on-vercel.vercel.app%2F&amp;amp;demo-image=https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F876c502b-f015-414b-aa4c-81b222330300" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvercel.com%2Fbutton" alt="Deploy with Vercel" width="103" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>pdf</category>
      <category>puppeteer</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>How I finally stopped suffering from browser automation crashes</title>
      <dc:creator>Ivan Muñoz</dc:creator>
      <pubDate>Mon, 11 Aug 2025 12:45:03 +0000</pubDate>
      <link>https://forem.com/ivanalemunioz/how-i-finally-stopped-suffering-from-browser-automation-crashes-21cd</link>
      <guid>https://forem.com/ivanalemunioz/how-i-finally-stopped-suffering-from-browser-automation-crashes-21cd</guid>
      <description>&lt;p&gt;Let’s be real: browser automations crash. They crash often. And they crash when you least expect it—usually at 2 AM when you’re feeling smug about how “stable” your code is.&lt;/p&gt;

&lt;p&gt;I’ve been building automations since 2016. My journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2016:&lt;/strong&gt; Started with &lt;a href="https://www.electronjs.org/" rel="noopener noreferrer"&gt;Electron&lt;/a&gt;. Yeah, I know—it’s like using a bus to deliver a pizza. But it ran on my machine, so it worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2018:&lt;/strong&gt; Moved to &lt;a href="https://github.com/ariya/phantomjs" rel="noopener noreferrer"&gt;PhantomJS&lt;/a&gt;. It’s dead now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2021:&lt;/strong&gt; Finally found &lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;. Thank the dev gods it was still alive when I joined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every single tool had one thing in common:&lt;br&gt;
They all broke.&lt;br&gt;
And when they did, I had the same headache—finding out &lt;em&gt;what&lt;/em&gt; was going on inside the browser at the exact moment of the crash.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sentry.io" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt; could tell me where my code broke. But if I was waiting for &lt;code&gt;#query&lt;/code&gt; to appear and it never did, I didn’t just want a stack trace—I wanted to &lt;em&gt;see&lt;/em&gt; what was there instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Old Way (a.k.a. The Painful Way)
&lt;/h2&gt;

&lt;p&gt;Here’s my old crash-debug workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On crash, take a screenshot and HTML dump.&lt;/li&gt;
&lt;li&gt;Save them to &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Save the URLs in my DB.&lt;/li&gt;
&lt;li&gt;Wait for a Sentry email.&lt;/li&gt;
&lt;li&gt;Go to the DB, find the row, click the links, download the files, open them… &lt;em&gt;repeat until I lose the will to live&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This wasn’t a problem for small projects. But once my automations started getting real traffic, crashes multiplied like rabbits. Suddenly I was spending more time &lt;em&gt;hunting&lt;/em&gt; crash data than actually fixing bugs.&lt;/p&gt;

&lt;p&gt;So I did what any lazy developer does when a process is too slow and annoying…&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Way (a.k.a. I Built My Own Tool)
&lt;/h2&gt;

&lt;p&gt;Introducing: &lt;strong&gt;&lt;a href="https://buglesstack.com/" rel="noopener noreferrer"&gt;Buglesstack&lt;/a&gt;&lt;/strong&gt; 🎺🐛&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpxs0xwe24f2icwnpet7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpxs0xwe24f2icwnpet7.jpg" alt="Buglesstack" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of the clunky manual process, every crash now sends one complete report straight to Buglesstack with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error message&lt;/li&gt;
&lt;li&gt;Error stack&lt;/li&gt;
&lt;li&gt;Browser URL at the crash moment&lt;/li&gt;
&lt;li&gt;Full-page screenshot&lt;/li&gt;
&lt;li&gt;Full HTML snapshot&lt;/li&gt;
&lt;li&gt;Any custom metadata I want&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now when something breaks, I just open Buglesstack, see everything in one place, and fix it right away.&lt;/p&gt;

&lt;p&gt;The funny thing? Crashes that used to sit on my to-do list for days now get fixed the same day. Not because they’re easier—because I’m no longer wasting time jumping between my DB, S3, and local files like a circus act.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Made It Public
&lt;/h2&gt;

&lt;p&gt;Once I saw how much faster this made my life, I thought, “Other devs probably hate their crash workflow too.”&lt;/p&gt;

&lt;p&gt;So I opened Buglesstack as Open Source, you can follow the &lt;a href="https://github.com/ivanalemunioz/buglesstack-app" rel="noopener noreferrer"&gt;instructions to install it&lt;/a&gt; or use the cloud version.&lt;/p&gt;

&lt;p&gt;Upcoming stuff: email notifications, crash archiving, filtering, grouping—you name it. I’m my own biggest user, so I’m constantly adding the things I wish it had yesterday.&lt;/p&gt;

&lt;p&gt;If you try it and have ideas, email me at &lt;strong&gt;&lt;a href="//mailto:ivan@buglesstack.com"&gt;ivan@buglesstack.com&lt;/a&gt;&lt;/strong&gt;. I’m all ears.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Happy Customers
&lt;/h2&gt;

&lt;p&gt;Thanks to Buglesstack, I can now instantly answer my customers when their automation fails.&lt;/p&gt;

&lt;p&gt;Example: one crash report showed a “503 Service Unavailable” page. I just screenshotted the report preview and said:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feipvszvcrfuprwktemcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feipvszvcrfuprwktemcv.png" alt="503 Error" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi, it’s not us—the site you’re automating is down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s it. No digging. No guessing.&lt;br&gt;
The customer was happy. I was happy. Everyone wins.&lt;/p&gt;




&lt;p&gt;If your browser automations crash (and they will), don’t make debugging a treasure hunt. Give Buglesstack a spin.&lt;/p&gt;

</description>
      <category>puppeteer</category>
      <category>playwright</category>
      <category>developer</category>
      <category>selenium</category>
    </item>
    <item>
      <title>How to create an API to generate PDFs using Puppeteer with auto-deploy to AWS Lambda</title>
      <dc:creator>Ivan Muñoz</dc:creator>
      <pubDate>Mon, 21 Jul 2025 11:09:42 +0000</pubDate>
      <link>https://forem.com/ivanalemunioz/how-to-create-an-api-to-generate-pdfs-using-puppeteer-with-auto-deploy-to-aws-lambda-4hof</link>
      <guid>https://forem.com/ivanalemunioz/how-to-create-an-api-to-generate-pdfs-using-puppeteer-with-auto-deploy-to-aws-lambda-4hof</guid>
      <description>&lt;p&gt;&lt;em&gt;Original Article: &lt;a href="https://buglesstack.com/blog/puppeteer-pdf-aws-lambda-auto-deploy-using-github-actions/" rel="noopener noreferrer"&gt;How to create an API to generate PDFs using Puppeteer with auto-deploy to AWS Lambda&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In this article, we will explore how to set up an API to generate PDFs using Puppeteer, with auto-deploy to AWS Lambda using GitHub Actions.&lt;/p&gt;

&lt;p&gt;It includes a repo that serves as a base template for deploying a Puppeteer project to AWS Lambda using GitHub Actions. The repo is designed to convert your Puppeteer project into an API that can be invoked via HTTP requests, allowing you to run Puppeteer scripts in a serverless environment.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Development

&lt;ol&gt;
&lt;li&gt;Cloning the repository&lt;/li&gt;
&lt;li&gt;Installing dependencies&lt;/li&gt;
&lt;li&gt;Creating the .env file&lt;/li&gt;
&lt;li&gt;Running the development server&lt;/li&gt;
&lt;li&gt;Testing the API&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
Deployment

&lt;ol&gt;
&lt;li&gt;Creating the Lambda function&lt;/li&gt;
&lt;li&gt;Creating the S3 bucket&lt;/li&gt;
&lt;li&gt;Creating an IAM user and access keys&lt;/li&gt;
&lt;li&gt;Configuring your lambda function&lt;/li&gt;
&lt;li&gt;Configuring the GitHub Actions workflow&lt;/li&gt;
&lt;li&gt;Testing the deployment&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Additional Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cloning the repository
&lt;/h3&gt;

&lt;p&gt;Clone the repository to your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy.git
&lt;span class="nb"&gt;cd &lt;/span&gt;puppeteer-pdf-lambda-auto-deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Installing dependencies
&lt;/h3&gt;

&lt;p&gt;Install the required dependencies using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Creating the .env file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the root of the project based on the &lt;code&gt;.env.example&lt;/code&gt; file and fill in the required values.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Running the development server
&lt;/h3&gt;

&lt;p&gt;Run the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start the server on &lt;code&gt;http://localhost:5124&lt;/code&gt; and will watch for changes in the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Testing the API
&lt;/h3&gt;

&lt;p&gt;Test the API by sending a POST request to &lt;code&gt;http://localhost:5124/v1/pdf/html&lt;/code&gt; using a tool like Postman or curl. You should see the Puppeteer script running and returning a response.&lt;/p&gt;

&lt;p&gt;You can set the "options" parameter using the options from &lt;a href="https://pptr.dev/api/puppeteer.pdfoptions" rel="noopener noreferrer"&gt;https://pptr.dev/api/puppeteer.pdfoptions&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:5124/v1/pdf/html'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer YOUR_GENERATED_AUTH_TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "html": "&amp;lt;p style='&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'text-align:center'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;Hello World! &amp;lt;b&amp;gt;This PDF was created using &amp;lt;a href='&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy&amp;lt;/a&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;",
    "file_name": "Test PDF file",
    "options" : {}
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;You can jump to step 4 if you already have a Lambda function, S3 bucket and IAM role set up.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating the Lambda function
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/lambda/home" rel="noopener noreferrer"&gt;AWS Lambda console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Create function".&lt;/li&gt;
&lt;li&gt;Choose "Author from scratch".&lt;/li&gt;
&lt;li&gt;Enter a name for your function, e.g., &lt;code&gt;puppeteer-pdf-lambda-auto-deploy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the runtime as &lt;code&gt;Node.js 22.x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the architecture as &lt;code&gt;x86_64&lt;/code&gt;. It will not work with &lt;code&gt;arm64&lt;/code&gt; architecture.&lt;/li&gt;
&lt;li&gt;Under "Additional configurations", enable "Function URL" to allow HTTP access to your Lambda function.

&lt;ul&gt;
&lt;li&gt;"Auth type" select NONE.&lt;/li&gt;
&lt;li&gt;"Invoke mode" select "BUFFERED (default)".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on "Create function".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Creating the S3 bucket
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/s3/home" rel="noopener noreferrer"&gt;S3 console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Create bucket".&lt;/li&gt;
&lt;li&gt;As bucket type select "General purpose".&lt;/li&gt;
&lt;li&gt;Enter a unique name for your bucket, e.g., &lt;code&gt;puppeteer-pdf-lambda-auto-deploy-bucket&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Let the rest of the settings as default.&lt;/li&gt;
&lt;li&gt;Click on "Create bucket".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Creating an IAM user and access keys
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home" rel="noopener noreferrer"&gt;IAM console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Users" in the left sidebar.&lt;/li&gt;
&lt;li&gt;Click on "Create user".&lt;/li&gt;
&lt;li&gt;Enter a username, e.g., &lt;code&gt;puppeteer-pdf-lambda-auto-deploy-user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Next".&lt;/li&gt;
&lt;li&gt;Click on "Attach policies directly" and select the following policies:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWSLambda_FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonS3FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on "Create user".&lt;/li&gt;
&lt;li&gt;Click on the user you just created.&lt;/li&gt;
&lt;li&gt;Under the "Security credentials" tab, click on "Create access key".&lt;/li&gt;
&lt;li&gt;As Use case select "Other"&lt;/li&gt;
&lt;li&gt;Click on "Next".&lt;/li&gt;
&lt;li&gt;Click on "Create access key".&lt;/li&gt;
&lt;li&gt;Copy the &lt;code&gt;Access key ID&lt;/code&gt; and &lt;code&gt;Secret access key&lt;/code&gt; and store them securely.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Configuring your lambda function
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/lambda/home" rel="noopener noreferrer"&gt;AWS Lambda console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the function you created earlier.&lt;/li&gt;
&lt;li&gt;Under the "Configuration" tab, click on "Environment variables".&lt;/li&gt;
&lt;li&gt;Click in "Edit" and add the &lt;code&gt;BUGLESSTACK_ACCESS_TOKEN&lt;/code&gt; and &lt;code&gt;BROWSER_AUTOMATIONS_ACCESS_TOKEN&lt;/code&gt; variables. You can get more info about how to generate these tokens in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy/blob/main/.env.example" rel="noopener noreferrer"&gt;.env.example file&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;Optionally, if you want to store the PDFs in a s3 bucket add &lt;code&gt;S3_BUCKET_FOR_STORAGE&lt;/code&gt; variable and &lt;code&gt;S3_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;S3_SECRET_ACCESS_KEY&lt;/code&gt; and &lt;code&gt;S3_REGION&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Under the "Configuration" tab, click on "General configuration" and "Edit".&lt;/li&gt;
&lt;li&gt;Increase the timeout to 2 minutes and memory to 1024MB (recommended) and click on "Save".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Configuring the GitHub Actions workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your GitHub repository.&lt;/li&gt;
&lt;li&gt;Click on "Settings" in the top menu.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click on "Secrets and variables" and then "Actions".&lt;/li&gt;
&lt;li&gt;Add the following secrets:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;: Your AWS access key ID generated in step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;: Your AWS secret access key generated in step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_REGION&lt;/code&gt;: The AWS region where your Lambda function is deployed (e.g., &lt;code&gt;us-east-1&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the same section, click on "Variables" and add the following variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;S3_BUCKET&lt;/code&gt;: The name of the S3 bucket where the Lambda function package will be uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_KEY&lt;/code&gt;: The key (path) in the S3 bucket where the Lambda function package will be stored (e.g., &lt;code&gt;lambda/puppeteer.zip&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_LAYER_BUCKET&lt;/code&gt;: The name of the S3 bucket where the Lambda layer package will be uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_LAYER_KEY&lt;/code&gt;: The key (path) in the S3 bucket where the Lambda layer package will be stored (e.g., &lt;code&gt;layers/puppeteer.zip&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LAYER_NAME&lt;/code&gt;: The name of the Lambda layer to be created or updated (e.g., &lt;code&gt;puppeteer-pdf-lambda-auto-deploy-layer&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LAMBDA_FUNCTION_NAME&lt;/code&gt;: The name of the Lambda function to be updated (e.g., &lt;code&gt;puppeteer-pdf-lambda-auto-deploy&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In "Settings &amp;gt; Actions &amp;gt; General", ensure that "Allow all actions and reusable workflows" is selected under "Actions permissions".&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6. Testing the deployment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Push your changes to the &lt;code&gt;main&lt;/code&gt; branch of your GitHub repository.&lt;/li&gt;
&lt;li&gt;The GitHub Actions workflow will automatically build and deploy your Puppeteer project to AWS Lambda.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the workflow is complete, you can test the API by sending a POST request to &lt;code&gt;https://YOUR_LAMBDA_URL/v1/pdf/html&lt;/code&gt; (you can get your Lambda URL in the Lambda details) using a tool like Postman or curl. You should see the Puppeteer script running and returning a response.&lt;/p&gt;

&lt;p&gt;You can set the "options" parameter using the options from &lt;a href="https://pptr.dev/api/puppeteer.pdfoptions" rel="noopener noreferrer"&gt;https://pptr.dev/api/puppeteer.pdfoptions&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'https://YOUR_LAMBDA_URL/v1/pdf/html'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer YOUR_GENERATED_AUTH_TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "html": "&amp;lt;p style='&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'text-align:center'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;Hello World! &amp;lt;b&amp;gt;This PDF was created using &amp;lt;a href='&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy&amp;lt;/a&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;",
    "file_name": "Test PDF file",
    "options" : {}
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you encounter issues with Puppeteer not launching or crashing, ensure that the Lambda function has sufficient memory allocated (at least 1024 MB is recommended).&lt;/li&gt;
&lt;li&gt;Configure the Lambda function timeout to a reasonable value (e.g., 30 seconds) to allow Puppeteer enough time to execute.&lt;/li&gt;
&lt;li&gt;Ensure your Lambda architecture is set to &lt;code&gt;x86_64&lt;/code&gt;, it will not work with &lt;code&gt;arm64&lt;/code&gt; architecture.&lt;/li&gt;
&lt;li&gt;Ensure your Lambda and S3 bucket are in the same region.&lt;/li&gt;
&lt;li&gt;Check the permissions of the IAM user to ensure it has access to S3 and Lambda.&lt;/li&gt;
&lt;li&gt;Check the AWS Lambda logs in CloudWatch Logs for any errors or issues during execution.&lt;/li&gt;
&lt;li&gt;If you encounter issues with the Github Actions workflow, check the workflow logs for any errors or issues during the build and deployment process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this guide, you learned how to set up an API to create PDFs with Puppeteer, and a CI/CD pipeline using GitHub Actions and AWS Lambda. By following these steps, you can automate the deployment of your Puppeteer scripts to AWS Lambda, making it easier to run headless browser tasks in the cloud. This setup not only streamlines your development workflow but also leverages the scalability and reliability of AWS Lambda for running your Puppeteer scripts.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also explore the &lt;a href="https://buglesstack.com/" rel="noopener noreferrer"&gt;Buglesstack&lt;/a&gt; integration for error tracking and monitoring, which is included in the project.&lt;/p&gt;

&lt;p&gt;You can also check the &lt;a href="https://github.com/ivanalemunioz/puppeteer-pdf-lambda-auto-deploy/blob/main/.github/workflows/deploy-lambda.yml" rel="noopener noreferrer"&gt;GitHub Actions workflow file&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" rel="noopener noreferrer"&gt;AWS Lambda Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;Puppeteer Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.buglesstack.com/" rel="noopener noreferrer"&gt;Buglesstack Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>pdf</category>
      <category>api</category>
      <category>lambda</category>
    </item>
    <item>
      <title>How to auto-deploy Puppeteer in AWS Lambda using Github actions</title>
      <dc:creator>Ivan Muñoz</dc:creator>
      <pubDate>Sat, 19 Jul 2025 12:12:20 +0000</pubDate>
      <link>https://forem.com/ivanalemunioz/how-to-auto-deploy-puppeteer-in-aws-lambda-using-github-actions-46g1</link>
      <guid>https://forem.com/ivanalemunioz/how-to-auto-deploy-puppeteer-in-aws-lambda-using-github-actions-46g1</guid>
      <description>&lt;p&gt;&lt;em&gt;Original Article: &lt;a href="https://buglesstack.com/blog/puppeteer-aws-lambda-auto-deploy-using-github-actions/" rel="noopener noreferrer"&gt;How to auto-deploy Puppeteer in AWS Lambda using Github actions&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In this article, we will explore how to set up a Puppeteer project that can be automatically deployed to AWS Lambda using GitHub Actions.&lt;/p&gt;

&lt;p&gt;It includes a repo that serves as a base template for deploying a Puppeteer project to AWS Lambda using GitHub Actions. The repo is designed to convert your Puppeteer project into an API that can be invoked via HTTP requests, allowing you to run Puppeteer scripts in a serverless environment.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-lambda-auto-deploy" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Development

&lt;ol&gt;
&lt;li&gt;Cloning the repository&lt;/li&gt;
&lt;li&gt;Installing dependencies&lt;/li&gt;
&lt;li&gt;Creating the .env file&lt;/li&gt;
&lt;li&gt;Running the development server&lt;/li&gt;
&lt;li&gt;Testing the API&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
Deployment

&lt;ol&gt;
&lt;li&gt;Creating the Lambda function&lt;/li&gt;
&lt;li&gt;Creating the S3 bucket&lt;/li&gt;
&lt;li&gt;Creating an IAM user and access keys&lt;/li&gt;
&lt;li&gt;Configuring your lambda function&lt;/li&gt;
&lt;li&gt;Configuring the GitHub Actions workflow&lt;/li&gt;
&lt;li&gt;Testing the deployment&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Additional Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cloning the repository
&lt;/h3&gt;

&lt;p&gt;Clone the repository to your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ivanalemunioz/puppeteer-lambda-auto-deploy.git
&lt;span class="nb"&gt;cd &lt;/span&gt;puppeteer-lambda-auto-deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Installing dependencies
&lt;/h3&gt;

&lt;p&gt;Install the required dependencies using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Creating the .env file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the root of the project based on the &lt;code&gt;.env.example&lt;/code&gt; file and fill in the required values.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Running the development server
&lt;/h3&gt;

&lt;p&gt;Run the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start the server on &lt;code&gt;http://localhost:5123&lt;/code&gt; and will watch for changes in the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Testing the API
&lt;/h3&gt;

&lt;p&gt;You can test the API by sending a POST request to &lt;code&gt;http://localhost:5123/v1/run&lt;/code&gt; using a tool like Postman or curl. You should see the Puppeteer script running and returning a response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:5123/v1/run'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer YOUR_GENERATED_AUTH_TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "action": "scrape-pptr-docs",
    "params": {}
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;You can jump to step 4 if you already have a Lambda function, S3 bucket and IAM role set up.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating the Lambda function
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/lambda/home" rel="noopener noreferrer"&gt;AWS Lambda console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Create function".&lt;/li&gt;
&lt;li&gt;Choose "Author from scratch".&lt;/li&gt;
&lt;li&gt;Enter a name for your function, e.g., &lt;code&gt;puppeteer-lambda-auto-deploy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the runtime as &lt;code&gt;Node.js 22.x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the architecture as &lt;code&gt;x86_64&lt;/code&gt;. It will not work with &lt;code&gt;arm64&lt;/code&gt; architecture.&lt;/li&gt;
&lt;li&gt;Under "Additional configurations", enable "Function URL" to allow HTTP access to your Lambda function.

&lt;ul&gt;
&lt;li&gt;"Auth type" select NONE.&lt;/li&gt;
&lt;li&gt;"Invoke mode" select "BUFFERED (default)".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on "Create function".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Creating the S3 bucket
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/s3/home" rel="noopener noreferrer"&gt;S3 console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Create bucket".&lt;/li&gt;
&lt;li&gt;As bucket type select "General purpose".&lt;/li&gt;
&lt;li&gt;Enter a unique name for your bucket, e.g., &lt;code&gt;puppeteer-lambda-auto-deploy-bucket&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Let the rest of the settings as default.&lt;/li&gt;
&lt;li&gt;Click on "Create bucket".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Creating an IAM user and access keys
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home" rel="noopener noreferrer"&gt;IAM console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Users" in the left sidebar.&lt;/li&gt;
&lt;li&gt;Click on "Create user".&lt;/li&gt;
&lt;li&gt;Enter a username, e.g., &lt;code&gt;puppeteer-lambda-auto-deploy-user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click on "Next".&lt;/li&gt;
&lt;li&gt;Click on "Attach policies directly" and select the following policies:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWSLambda_FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonS3FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on "Create user".&lt;/li&gt;
&lt;li&gt;Click on the user you just created.&lt;/li&gt;
&lt;li&gt;Under the "Security credentials" tab, click on "Create access key".&lt;/li&gt;
&lt;li&gt;As Use case select "Other"&lt;/li&gt;
&lt;li&gt;Click on "Next".&lt;/li&gt;
&lt;li&gt;Click on "Create access key".&lt;/li&gt;
&lt;li&gt;Copy the &lt;code&gt;Access key ID&lt;/code&gt; and &lt;code&gt;Secret access key&lt;/code&gt; and store them securely.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Configuring your lambda function
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/lambda/home" rel="noopener noreferrer"&gt;AWS Lambda console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the function you created earlier.&lt;/li&gt;
&lt;li&gt;Under the "Configuration" tab, click on "Environment variables".&lt;/li&gt;
&lt;li&gt;Click in "Edit" and add the &lt;code&gt;BUGLESSTACK_ACCESS_TOKEN&lt;/code&gt; and &lt;code&gt;BROWSER_AUTOMATIONS_ACCESS_TOKEN&lt;/code&gt; variables. You can get more info about how to generate these tokens in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-lambda-auto-deploy/blob/main/.env.example" rel="noopener noreferrer"&gt;.env.example file&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Under the "Configuration" tab, click on "General configuration" and "Edit".&lt;/li&gt;
&lt;li&gt;Increase the timeout to 2 minutes and memory to 1024MB (recommended) and click on "Save".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Configuring the GitHub Actions workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your GitHub repository.&lt;/li&gt;
&lt;li&gt;Click on "Settings" in the top menu.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click on "Secrets and variables" and then "Actions".&lt;/li&gt;
&lt;li&gt;Add the following secrets:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;: Your AWS access key ID generated in step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;: Your AWS secret access key generated in step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_REGION&lt;/code&gt;: The AWS region where your Lambda function is deployed (e.g., &lt;code&gt;us-east-1&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the same section, click on "Variables" and add the following variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;S3_BUCKET&lt;/code&gt;: The name of the S3 bucket where the Lambda function package will be uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_KEY&lt;/code&gt;: The key (path) in the S3 bucket where the Lambda function package will be stored (e.g., &lt;code&gt;lambda/puppeteer.zip&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_LAYER_BUCKET&lt;/code&gt;: The name of the S3 bucket where the Lambda layer package will be uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_LAYER_KEY&lt;/code&gt;: The key (path) in the S3 bucket where the Lambda layer package will be stored (e.g., &lt;code&gt;layers/puppeteer.zip&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LAYER_NAME&lt;/code&gt;: The name of the Lambda layer to be created or updated (e.g., &lt;code&gt;puppeteer-lambda-auto-deploy-layer&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LAMBDA_FUNCTION_NAME&lt;/code&gt;: The name of the Lambda function to be updated (e.g., &lt;code&gt;puppeteer-lambda-auto-deploy&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In "Settings &amp;gt; Actions &amp;gt; General", ensure that "Allow all actions and reusable workflows" is selected under "Actions permissions".&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6. Testing the deployment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Push your changes to the &lt;code&gt;main&lt;/code&gt; branch of your GitHub repository.&lt;/li&gt;
&lt;li&gt;The GitHub Actions workflow will automatically build and deploy your Puppeteer project to AWS Lambda.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the workflow is complete, you can test the API by sending a POST request to &lt;code&gt;https://YOUR_LAMBDA_URL/v1/run&lt;/code&gt; (you can get your Lambda URL in the Lambda details) using a tool like Postman or curl. You should see the Puppeteer script running and returning a response.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'https://YOUR_LAMBDA_URL/v1/run'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer YOUR_GENERATED_AUTH_TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "action": "scrape-pptr-docs",
    "params": {}
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you encounter issues with Puppeteer not launching or crashing, ensure that the Lambda function has sufficient memory allocated (at least 1024 MB is recommended).&lt;/li&gt;
&lt;li&gt;Configure the Lambda function timeout to a reasonable value (e.g., 30 seconds) to allow Puppeteer enough time to execute.&lt;/li&gt;
&lt;li&gt;Ensure your Lambda architecture is set to &lt;code&gt;x86_64&lt;/code&gt;, it will not work with &lt;code&gt;arm64&lt;/code&gt; architecture.&lt;/li&gt;
&lt;li&gt;Ensure your Lambda and S3 bucket are in the same region.&lt;/li&gt;
&lt;li&gt;Check the permissions of the IAM user to ensure it has access to S3 and Lambda.&lt;/li&gt;
&lt;li&gt;Check the AWS Lambda logs in CloudWatch Logs for any errors or issues during execution.&lt;/li&gt;
&lt;li&gt;If you encounter issues with the Github Actions workflow, check the workflow logs for any errors or issues during the build and deployment process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this guide, you learned how to set up a CI/CD pipeline for your Puppeteer project using GitHub Actions and AWS Lambda. By following these steps, you can automate the deployment of your Puppeteer scripts to AWS Lambda, making it easier to run headless browser tasks in the cloud. This setup not only streamlines your development workflow but also leverages the scalability and reliability of AWS Lambda for running your Puppeteer scripts.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/ivanalemunioz/puppeteer-lambda-auto-deploy" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also explore the &lt;a href="https://buglesstack.com/" rel="noopener noreferrer"&gt;Buglesstack&lt;/a&gt; integration for error tracking and monitoring, which is included in the project.&lt;/p&gt;

&lt;p&gt;You can also check the &lt;a href="https://github.com/ivanalemunioz/puppeteer-lambda-auto-deploy/blob/main/.github/workflows/deploy-lambda.yml" rel="noopener noreferrer"&gt;GitHub Actions workflow file&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" rel="noopener noreferrer"&gt;AWS Lambda Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;Puppeteer Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.buglesstack.com/" rel="noopener noreferrer"&gt;Buglesstack Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>puppeteer</category>
      <category>githubactions</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
