<?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>DEV Community: Julien Landuré</title>
    <description>The latest articles on DEV Community by Julien Landuré (@jlandure).</description>
    <link>https://dev.to/jlandure</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192673%2Fa76bdc95-3cd3-4bd5-b177-01f02dabcdb3.jpg</url>
      <title>DEV Community: Julien Landuré</title>
      <link>https://dev.to/jlandure</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jlandure"/>
    <language>en</language>
    <item>
      <title>Your Face on a World Cup Sticker: Our Nano Banana Story</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 28 Jul 2026 11:09:48 +0000</pubDate>
      <link>https://dev.to/gde/your-face-on-a-world-cup-sticker-our-nano-banana-story-43p6</link>
      <guid>https://dev.to/gde/your-face-on-a-world-cup-sticker-our-nano-banana-story-43p6</guid>
      <description>&lt;p&gt;There is something timeless about opening a pack of football stickers. You never know which player, celebration, or rare edition you will find next.&lt;/p&gt;

&lt;p&gt;For the 2026 World Cup, we wanted to recreate that feeling with a personal twist: what if the player on the sticker was you, your friends, or your family? That simple idea became &lt;a href="https://sticker.techtown.fr/" rel="noopener noreferrer"&gt;Sticker Studio&lt;/a&gt;, a small experiment powered by Google's Nano Banana image model.&lt;/p&gt;

&lt;p&gt;Take a photo, choose a national team, add your name, and the application transforms your face into a playful cartoon football sticker. In this article, we'll explore the idea, the prompt system behind every scenario, the reference images that fixed our 2026 jerseys, and why we upgraded from Nano Banana 1 to Nano Banana 2.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrecuitwfii2npmnwyet.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrecuitwfii2npmnwyet.png" alt="Friends and family become World Cup 2026 cartoon collectibles with Sticker Studio." width="800" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚽ 1. The idea: putting our own faces on the stickers
&lt;/h2&gt;

&lt;p&gt;The starting point was not a technical challenge. It was fun.&lt;/p&gt;

&lt;p&gt;We wanted the nostalgia of football sticker albums without simply reproducing a traditional player portrait. Each result had to feel personal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the face should remain recognizable.&lt;/li&gt;
&lt;li&gt;the rendering should look like a playful children's illustration, not a photorealistic edit.&lt;/li&gt;
&lt;li&gt;the person should wear their selected national team's kit.&lt;/li&gt;
&lt;li&gt;every sticker should tell a small story.&lt;/li&gt;
&lt;li&gt;rare borders and special effects should bring back the excitement of opening a pack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The experience is deliberately simple. You provide a photo and a name, choose your team, and let the application create the surprise.&lt;/p&gt;

&lt;p&gt;Behind that simple flow, the application runs an Astro frontend on Firebase Hosting and an API on Cloud Run. The browser sends the photo and the user's choices to the backend. The backend selects the scenario, builds the complete prompt, attaches the relevant references, and calls the Gemini image-generation API.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv06iv7dlpbncobde433n.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv06iv7dlpbncobde433n.png" alt="The public Studio keeps the experience simple: one photo, one team, one personalized sticker." width="609" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The AI is asked to preserve the real identity cues visible in the photo — face shape, skin tone, hairstyle, facial hair, eyebrows, expression, and accessories such as glasses. At the same time, it must turn that identity into a simplified, expressive cartoon.&lt;/p&gt;

&lt;p&gt;This balance matters. Too realistic, and the result loses its friendly illustrated character. Too generic, and the person no longer recognizes themselves. The fun happens in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✍️ 2. One prompt engine, dozens of scenarios
&lt;/h2&gt;

&lt;p&gt;A prompt like “turn this person into a football sticker” can produce an image. It does not produce a consistent collection.&lt;/p&gt;

&lt;p&gt;To make stickers generated for different people feel as if they belong to the same album, we built every prompt from the same six-part structure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subject and identity&lt;/strong&gt; — who the person is and which visible traits must be preserved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kit&lt;/strong&gt; — the selected national team and its official football jersey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt; — the humorous pose or celebration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment&lt;/strong&gt; — the stadium, pitch, city, or imaginary setting around the player.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style and card layout&lt;/strong&gt; — the shared visual rules, border, typography, and rarity effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output constraints&lt;/strong&gt; — a centered, vertical, full-bleed composition suitable for a sticker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the backend, those layers are assembled in TypeScript. Here is a shortened version of the production function:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildStickerPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PromptOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PLAYER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&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;styleBible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleBible&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;DEFAULT_STYLE_BIBLE&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Using the attached real photo as the identity reference...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`The character wears the official kit of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;teamName&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="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referenceInstruction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&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="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referenceInstruction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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="s2"&gt;`Scenario: the character is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&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="s2"&gt;`Presented inside a trading-card layout with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;borderPhrase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;border&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="s2"&gt;`At the bottom, render the name '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' on one readable line.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;styleBible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Center the subject. Use a vertical, full-bleed composition.&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function keeps the permanent art direction in one place, then injects only the values that make a sticker unique. It also makes optional instructions explicit: a jersey reference, trophy, ball, or rare effect is included only when the selected slot needs it.&lt;/p&gt;

&lt;p&gt;The scenarios provide variety. One person can perform a bicycle kick in a packed stadium. Another can ride a rocket-powered football through space, take a victory selfie, attempt a Panenka, meditate on the pitch, or lift the trophy under golden confetti.&lt;/p&gt;

&lt;p&gt;Here is a shortened example based on the real prompt structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using the attached real photo as the identity reference, transform this exact person into a humorous parodic cartoon caricature. Match their real face closely: face shape, skin tone, hairstyle, facial hair, eyebrows, and expression.

The character is a professional soccer player wearing the official kit of the France national football team.

Scenario: the character is performing a spectacular overhead bicycle
kick in mid-air, with the ball blurring off the foot and a packed
stadium behind.

Presented inside a rectangular trading-card layout with a vibrant blue parallel border. At the top, arched text reads 'WORLD CUP 2026'. At the bottom, the name 'JULIEN' is fully readable.

Art style: die-cut sticker design, bold clean white outer outline,
2D vector illustration, flat colors, simple cel shading, thick black
ink outlines, high contrast, and playful humorous cartoon caricature.

Center the subject. Use a vertical, full-bleed composition.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shared visual instructions are our &lt;strong&gt;Style Bible&lt;/strong&gt;. They keep the thick outlines, flat colors, card frame, typography, and composition stable across all generations.&lt;/p&gt;

&lt;p&gt;We also randomize collectible details. Common stickers appear more frequently, while gold, silver, black, and holographic borders are less common. Some generations receive a World Cup trophy or official ball as a secondary prop. Around one sticker in twenty gets a golden &lt;code&gt;LEGENDAIRE&lt;/code&gt; badge.&lt;/p&gt;

&lt;p&gt;The result is more than a photo filter. The same input can become a genuinely different collectible each time.&lt;/p&gt;

&lt;p&gt;💪 &lt;strong&gt;ProTip:&lt;/strong&gt; separate stable visual rules from variable scenario instructions. It makes your prompt system easier to tune and prevents a change to one celebration from altering the visual identity of the whole collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  👕 3. When the AI remembered the wrong jerseys
&lt;/h2&gt;

&lt;p&gt;The first stickers looked promising, but one detail immediately broke the illusion: the kits were wrong. For example, this result clearly uses an older France jersey.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyajox2vwz1xzxvycwyb1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyajox2vwz1xzxvycwyb1.jpg" alt="Without a jersey reference, Nano Banana falls back to an older France kit." width="651" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nano Banana often generated older designs, including 2024-era jerseys. That is understandable — a text prompt can describe “the official 2026 France home kit,” but the model still needs reliable visual information about a new design.&lt;/p&gt;

&lt;p&gt;We solved this with multimodal reference images.&lt;/p&gt;

&lt;p&gt;For France, England, Spain, and Argentina, the backend now attaches a packshot of the official 2026 home jersey to the generation request. The prompt explicitly tells Nano Banana to reproduce its colors, pattern, collar, marks, and crest placement in the cartoon style.&lt;/p&gt;

&lt;p&gt;It also draws a strict boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This image is ONLY a clothing reference. Ignore any person, mannequin,
face, body, pose, or background shown in it, and use it solely for the
jersey design.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We applied the same technique to the official 2026 match ball. Whenever a scenario includes a football, the backend sends the ball image and asks the model to reproduce its base color, panel shapes, colored zones, and line patterns.&lt;/p&gt;

&lt;p&gt;The complete request can therefore contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user's portrait for identity;&lt;/li&gt;
&lt;li&gt;a 2026 jersey packshot for clothing;&lt;/li&gt;
&lt;li&gt;the official match-ball image for the ball design;&lt;/li&gt;
&lt;li&gt;the assembled text prompt for the scene and visual rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual Gemini request follows that order:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&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="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;portrait&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;jersey&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jersey&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="nx"&gt;ball&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ball&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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&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;response&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;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DEFAULT_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;imageConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3:4&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering is intentional: the portrait remains the primary identity reference, followed by controlled visual assets and finally the instructions that explain how to use them. All of this happens on the backend. The browser sends the photo and business choices, but it never receives the Gemini API key or controls the raw production prompt.&lt;/p&gt;

&lt;p&gt;There was one important gotcha. A jersey reference containing a recognizable face or head could make the image request fail with &lt;code&gt;finishReason=IMAGE_OTHER&lt;/code&gt; and no generated image.&lt;/p&gt;

&lt;p&gt;⚡️ &lt;strong&gt;ProTip:&lt;/strong&gt; prepare product references as clean packshots. Crop jersey images to the torso, remove faces and heads, keep the background simple, and use a reasonably sized compressed image. Reference quality directly affects both reliability and adherence.&lt;/p&gt;

&lt;h2&gt;
  
  
  🍌 4. Moving from Nano Banana 1 to Nano Banana 2
&lt;/h2&gt;

&lt;p&gt;Our first production version used Nano Banana 1 through &lt;code&gt;gemini-2.5-flash-image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It proved that the concept worked. It could transform a portrait, create the cartoon style, and follow imaginative football scenarios. But as our prompts gained more constraints, we needed stronger adherence: preserve the person's identity, copy a new jersey, use the correct ball, render the name, respect the card frame, and still create a dynamic scene.&lt;/p&gt;

&lt;p&gt;We upgraded the production model to Nano Banana 2, exposed through &lt;code&gt;gemini-3.1-flash-image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The improvement was not about adding more adjectives to the prompt. Nano Banana 2 followed the combined identity, reference-image, text, and layout instructions more reliably. That made it a better fit for a product in which small details — the correct collar, visible glasses, or an uncropped name — determine whether the sticker feels right.&lt;/p&gt;

&lt;p&gt;We compared both versions with the same portraits, scenarios, and prompts before switching. Keeping the model name configurable also means we can repeat those A/B tests as the Gemini image family evolves.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6qjzcltd9mu07e1acsg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6qjzcltd9mu07e1acsg.png" alt="A controlled A/B comparison made reference adherence and layout differences easier to evaluate." width="583" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generation still uses a 1K output. For a fast, playful web experience, this gives us a practical balance between visual quality, latency, and cost.&lt;/p&gt;

&lt;p&gt;You can learn more about image generation in the &lt;a href="https://ai.google.dev/gemini-api/docs/image-generation" rel="noopener noreferrer"&gt;official Gemini API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 5. From a side project to real API limits
&lt;/h2&gt;

&lt;p&gt;The first audience was exactly who we built it for: friends and family.&lt;/p&gt;

&lt;p&gt;Then people started generating several versions, trying another team, looking for a rarer border, and sharing the funniest results. That is great product feedback — and a fast way to discover API quotas.&lt;/p&gt;

&lt;p&gt;At Gemini API Tier 1, our daily request limits became a real constraint. The API key is only the credential; the effective quota comes from the associated Gemini API project and usage tier. Once the studio became active, generation capacity mattered as much as prompt quality.&lt;/p&gt;

&lt;p&gt;A big thank you to &lt;strong&gt;Guillaume Vernade&lt;/strong&gt;, who helped us move to Tier 3 and give the experiment enough room for more people to play.&lt;/p&gt;

&lt;p&gt;This was a useful reminder: when an AI demo becomes shareable, usage can grow differently from a traditional application. One visit may trigger several expensive generation calls because exploration is part of the experience. Quotas, concurrency, retries, and graceful error messages belong in the product design from the beginning.&lt;/p&gt;

&lt;p&gt;Thanks to a spending cap, we can also control how much we spend on new generations. Each sticker costs approximately &lt;strong&gt;$0.20&lt;/strong&gt; to generate, so the cap protects the experiment from an unexpectedly high bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion: now create your World Cup sticker
&lt;/h2&gt;

&lt;p&gt;Sticker Studio started with one simple question: what if our friends and family were the players inside the pack?&lt;/p&gt;

&lt;p&gt;Building it taught us three practical lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured prompts turn isolated images into a coherent collection;&lt;/li&gt;
&lt;li&gt;current visual references are essential when recent jerseys and equipment must be accurate;&lt;/li&gt;
&lt;li&gt;a stronger model and enough API capacity matter once people start experimenting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the main result is much simpler: it is fun.&lt;/p&gt;

&lt;p&gt;Friends and family have already generated &lt;strong&gt;365 stickers&lt;/strong&gt;. With the World Cup final finished, now is the perfect time to create yours, try another team, compare rare editions, and share the result.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sticker.techtown.fr/" rel="noopener noreferrer"&gt;Create your own World Cup sticker with Sticker Studio&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which team, scenario, and rarity will you get?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;IMPORTANT NOTE: Sticker Studio is a playful, unofficial fan project and is not affiliated with Panini or FIFA.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nanobanana</category>
      <category>gemini</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>How to celebrate an achievement as an open source developer?</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 30 Jun 2020 14:40:05 +0000</pubDate>
      <link>https://dev.to/jlandure/how-to-celebrate-an-achievement-as-an-open-source-developer-19jo</link>
      <guid>https://dev.to/jlandure/how-to-celebrate-an-achievement-as-an-open-source-developer-19jo</guid>
      <description>&lt;p&gt;✏️ As an open source developer, I've maintained on GitHub a docker image since July 2017: an &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;"Alpine Chrome"&lt;/a&gt; and now the image has been pulled more than 🎉 &lt;strong&gt;1 000 000 times&lt;/strong&gt; 🎉 on the Docker Hub.&lt;/p&gt;

&lt;p&gt;I wrote about it recently on Dev.to 👇&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/zenika" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F1234%2F61529416-7337-4c59-ab27-aaa666d850fc.png" alt="Zenika" width="800" height="703"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F192673%2Fa76bdc95-3cd3-4bd5-b177-01f02dabcdb3.jpg" alt="" width="450" height="450"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Crafting the perfect container to play with a Headless Chrome&lt;/h2&gt;
      &lt;h3&gt;Julien Landuré for Zenika ・ May 26 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  🎯 What can I do to celebrate this achievement?
&lt;/h1&gt;

&lt;p&gt;🤔 I do not know what kind of celebration I could do! It's a big achievement from my point of view.&lt;br&gt;
🚀 I've started with a tweet here&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1277969952415215617-374" src="https://platform.twitter.com/embed/Tweet.html?id=1277969952415215617"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1277969952415215617-374');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1277969952415215617&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;😊 &lt;em&gt;Disclaimer: I am not a designer and I've spent too much time designing the arrows to highlight the &lt;code&gt;docker pulls: 1M&lt;/code&gt; information. Please do not judge it... and I've successfully added a &lt;code&gt;:rocket:&lt;/code&gt; emoji on the screenshot.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🎉 What else?
&lt;/h1&gt;

&lt;p&gt;💚 Do you have any kind of ideas or stories to share about celebrating your open source achievements?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a GIF?&lt;/li&gt;
&lt;li&gt;Invite my friends to celebrate it in a restaurant?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;[your turn]&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>github</category>
      <category>motivation</category>
      <category>docker</category>
    </item>
    <item>
      <title>⏱ 3 minutes to deploy your static Nuxt.js app on AppEngine</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 09 Jun 2020 12:53:17 +0000</pubDate>
      <link>https://dev.to/zenika/3-minutes-to-deploy-your-static-nuxt-js-app-on-appengine-2a2n</link>
      <guid>https://dev.to/zenika/3-minutes-to-deploy-your-static-nuxt-js-app-on-appengine-2a2n</guid>
      <description>&lt;p&gt;I’ve been deploying applications on &lt;a href="https://en.wikipedia.org/wiki/Serverless_computing" rel="noopener noreferrer"&gt;serverless solutions&lt;/a&gt; since 2010. Recently, I discovered &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; and I wanted to create this post on how to quickly set up a static Nuxt.js app on &lt;a href="https://cloud.google.com/appengine" rel="noopener noreferrer"&gt;AppEngine, the Google Cloud PaaS&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  📝 Getting started with Nuxt.js
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; is described as &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Intuitive Vue Framework&lt;br&gt;
Build your next Vue.js application with confidence using NuxtJS.&lt;br&gt;
An open source framework making web development simple and powerful. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's very powerful! You can use it to create various architectures: &lt;a href="https://nuxtjs.org/guide#server-rendered-universal-ssr-" rel="noopener noreferrer"&gt;SSR&lt;/a&gt;, &lt;a href="https://nuxtjs.org/guide#single-page-applications-spa-" rel="noopener noreferrer"&gt;SPA&lt;/a&gt; or &lt;a href="https://nuxtjs.org/guide#static-generated-pre-rendering-" rel="noopener noreferrer"&gt;Static&lt;/a&gt; renderings.&lt;br&gt;
For this demo, we will be focused on the &lt;strong&gt;static feature&lt;/strong&gt; using the &lt;code&gt;nuxt generate&lt;/code&gt; command.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚗ Create the project
&lt;/h2&gt;

&lt;p&gt;For the demo, we will use a fresh new project. To do this, execute the &lt;code&gt;npx create-nuxt-app&lt;/code&gt; command to create a &lt;a href="https://nuxtjs.org/guide/installation#using-code-create-nuxt-app-code-" rel="noopener noreferrer"&gt;simple Nuxt.js application&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;&lt;span class="nv"&gt;$ &lt;/span&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
v12.16.3

&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-nuxt-app

create-nuxt-app v2.15.0
✨  Generating Nuxt.js project &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
? Project name nuxt-static-deploy-appengine
? Project description My glorious Nuxt.js project
? Author name Julien Landuré
? Choose programming language TypeScript
? Choose the package manager Npm
? Choose UI framework None
? Choose custom server framework None &lt;span class="o"&gt;(&lt;/span&gt;Recommended&lt;span class="o"&gt;)&lt;/span&gt;
? Choose the runtime &lt;span class="k"&gt;for &lt;/span&gt;TypeScript Default
? Choose Nuxt.js modules &lt;span class="o"&gt;(&lt;/span&gt;Press &amp;lt;space&amp;gt; to &lt;span class="k"&gt;select&lt;/span&gt;, &amp;lt;a&amp;gt; to toggle all, &amp;lt;i&amp;gt; to inv
ert selection&lt;span class="o"&gt;)&lt;/span&gt;
? Choose linting tools Prettier
? Choose &lt;span class="nb"&gt;test &lt;/span&gt;framework None
? Choose rendering mode Universal &lt;span class="o"&gt;(&lt;/span&gt;SSR&lt;span class="o"&gt;)&lt;/span&gt;
? Choose development tools &lt;span class="o"&gt;(&lt;/span&gt;Press &amp;lt;space&amp;gt; to &lt;span class="k"&gt;select&lt;/span&gt;, &amp;lt;a&amp;gt; to toggle all, &amp;lt;i&amp;gt; to invert selection&lt;span class="o"&gt;)&lt;/span&gt;
...
🎉  Successfully created project nuxt-static-deploy-appengine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✅ Test locally
&lt;/h2&gt;

&lt;p&gt;As described in the command line, run the &lt;code&gt;npm run start&lt;/code&gt; &lt;a href="https://nuxtjs.org/guide/commands#development-environment" rel="noopener noreferrer"&gt;script to get your app running&lt;/a&gt; on &lt;code&gt;http://localhost:3000/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's edit the &lt;code&gt;index.vue&lt;/code&gt; and write this html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"subtitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello Nuxt on AppEngine 🎉&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it works! Very easy to see our modification instantly.&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%2Fi%2F7a5a4pg96lydb6y02cky.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%2Fi%2F7a5a4pg96lydb6y02cky.png" alt="Alt Text" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗 Build it
&lt;/h2&gt;

&lt;p&gt;To &lt;a href="https://nuxtjs.org/guide/commands#static-generated-deployment-pre-rendered-" rel="noopener noreferrer"&gt;build the app statically&lt;/a&gt;, run the &lt;code&gt;npm run generate&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run generate

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nuxt-static-deploy-appengine@1.0.0 generate .../nuxt-static-deploy-appengine
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nuxt generate

ℹ Production build                                                    10:52:36
✔ Builder initialized                                                 10:52:37
✔ Nuxt files generated                                                10:52:37
ℹ Starting &lt;span class="nb"&gt;type &lt;/span&gt;checking service...                   nuxt:typescript 10:52:39

✔ Client
  Compiled successfully &lt;span class="k"&gt;in &lt;/span&gt;6.95s

✔ Server
  Compiled successfully &lt;span class="k"&gt;in &lt;/span&gt;1.13s


Hash: c5e482cb9772cddb1178
Version: webpack 4.43.0
Time: 6951ms
Built at: 05/27/2020 10:52:46 AM
                         Asset       Size  Chunks                         Chunk Names
../server/client.manifest.json    6.6 KiB          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
       98f4a70ff71f0382866c.js   2.31 KiB       3  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  runtime
       9fc3f5e914943c5b3a9b.js   2.97 KiB       2  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  pages/index
                      LICENSES  389 bytes          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
       a51444c83f14a6afaeb5.js    153 KiB       1  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  commons.app
       d267ba93006b46dadaa8.js   49.2 KiB       0  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  app
 + 2 hidden assets
Entrypoint app &lt;span class="o"&gt;=&lt;/span&gt; 98f4a70ff71f0382866c.js a51444c83f14a6afaeb5.js d267ba93006b46dadaa8.js

Hash: fbd0c6af7579246a49e7
Version: webpack 4.43.0
Time: 1127ms
Built at: 05/27/2020 10:52:47 AM
                  Asset       Size  Chunks                         Chunk Names
818a9680ee5eeafdfb4b.js   3.05 KiB       1  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  pages/index
              server.js   26.7 KiB       0  &lt;span class="o"&gt;[&lt;/span&gt;emitted]              app
   server.manifest.json  145 bytes          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
Entrypoint app &lt;span class="o"&gt;=&lt;/span&gt; server.js
ℹ Generating pages                                                    
✔ Generated /                                                        
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✨ Congratulations, we built our first Nuxt.js application! &lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: We can check the files in the &lt;code&gt;dist&lt;/code&gt; folder or run it with &lt;code&gt;npx http-server dist&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  ☁️ Deploy it on AppEngine
&lt;/h1&gt;

&lt;p&gt;Then, to share the project with the rest of the world, we can deploy it on AppEngine. &lt;a href="https://cloud.google.com/appengine" rel="noopener noreferrer"&gt;Appengine&lt;/a&gt; comes with a lot of features and quotas. &lt;/p&gt;

&lt;p&gt;It is free for a basic usage thanks to its quotas BUT we have to enable billing to enjoy it. As a new customer, we get &lt;a href="https://cloud.google.com/free" rel="noopener noreferrer"&gt;$300 to spend on Google Cloud Platform products&lt;/a&gt; during your free trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛒 Create a project
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="//console.cloud.google.com"&gt;&lt;code&gt;console.cloud.google.com&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/building-app/creating-gcp-project" rel="noopener noreferrer"&gt;create a new Google Cloud Platform project&lt;/a&gt;.&lt;br&gt;
Please save the [PROJECT_ID] of the project: you have to replace the &lt;code&gt;nuxt-static-deploy&lt;/code&gt; with your [PROJECT_ID] for the rest of the post.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: we can retrieve the [PROJECT_ID] in the url of the web console like &lt;code&gt;https://console.cloud.google.com/appengine/start?project=nuxt-static-deploy&lt;/code&gt; where nuxt-static-deploy is my [PROJECT_ID].&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: To create a project to host your app, we can also use the &lt;code&gt;gcloud projects create&lt;/code&gt; command. we need to enter a [PROJECT_ID], &lt;code&gt;nuxt-static-deploy&lt;/code&gt; for example.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a project &lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud projects create nuxt-static-deploy 
Create &lt;span class="k"&gt;in &lt;/span&gt;progress &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;https://cloudresourcemanager.googleapis.com/v1/projects/nuxt-static-deploy].
Waiting &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;operations/cp.4929250122387435991] to finish...done.              
Enabling service &lt;span class="o"&gt;[&lt;/span&gt;cloudapis.googleapis.com] on project &lt;span class="o"&gt;[&lt;/span&gt;nuxt-static-deploy]...
Operation &lt;span class="s2"&gt;"operations/acf.803672e3-74b0-43e1-8a6b-3d37a4a0975b"&lt;/span&gt; finished successfully.

&lt;span class="c"&gt;# List the billing accounts&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud beta billing accounts list
ACCOUNT_ID            NAME                                          OPEN
00ADEE-870384-XXXXXX  my_account                                    True

&lt;span class="c"&gt;# Link the billing account to my project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud beta billing projects &lt;span class="nb"&gt;link &lt;/span&gt;nuxt-static-deploy &lt;span class="nt"&gt;--billing-account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00ADEE-870384-XXXXXX
billingAccountName: billingAccounts/00ADEE-870384-XXXXXX
billingEnabled: &lt;span class="nb"&gt;true
&lt;/span&gt;name: projects/nuxt-static-deploy/billingInfo
projectId: nuxt-static-deploy

&lt;span class="c"&gt;# Enable Cloud Build API&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud &lt;span class="nt"&gt;--project&lt;/span&gt; nuxt-static-deploy services &lt;span class="nb"&gt;enable &lt;/span&gt;cloudbuild.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✏️ Write the descriptor &lt;code&gt;app.yaml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;AppEngine supports various programming languages: python, go, java, php, nodejs etc.&lt;br&gt;
To deploy static files, the best practice is to use the &lt;code&gt;python37&lt;/code&gt; runtime.&lt;/p&gt;

&lt;p&gt;Let's create a file in &lt;code&gt;.appengine/app.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python37&lt;/span&gt;

&lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
  &lt;span class="na"&gt;static_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/(.*)&lt;/span&gt;
  &lt;span class="na"&gt;static_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;\1&lt;/span&gt;
  &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(.*)&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on the descriptor file, &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/config/appref" rel="noopener noreferrer"&gt;please read this doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't forget to copy the file in our &lt;code&gt;dist&lt;/code&gt; folder using this command &lt;code&gt;cp .appengine/app.yaml dist/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: We can add it to the &lt;code&gt;build&lt;/code&gt; script in your &lt;code&gt;package.json&lt;/code&gt; file.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Deploy your app
&lt;/h2&gt;

&lt;p&gt;If this is the first you are using the Google Cloud Platform, you have to install the &lt;code&gt;gcloud&lt;/code&gt; command line utility following &lt;a href="https://cloud.google.com/sdk/docs/quickstarts" rel="noopener noreferrer"&gt;the official page&lt;/a&gt; or &lt;a href="https://formulae.brew.sh/cask/google-cloud-sdk" rel="noopener noreferrer"&gt;using Homebrew&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deploy our app using the &lt;code&gt;gcloud app deploy&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app deploy &lt;span class="nt"&gt;--project&lt;/span&gt; nuxt-static-deploy dist 

You are creating an app &lt;span class="k"&gt;for &lt;/span&gt;project nuxt-static-deploy.
WARNING: Creating an App Engine application &lt;span class="k"&gt;for &lt;/span&gt;a project is irreversible and the region
cannot be changed. More information about regions is at
&amp;lt;https://cloud.google.com/appengine/docs/locations&amp;gt;.

Please choose the region where you want your App Engine application 
located:

 &lt;span class="o"&gt;[&lt;/span&gt;1] asia-east2    &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;2] asia-northeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;3] asia-northeast2 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;4] asia-northeast3 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;5] asia-south1   &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;6] australia-southeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;7] europe-west   &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;8] europe-west2  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;9] europe-west3  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;10] europe-west6  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;11] northamerica-northeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;12] southamerica-east1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;13] us-central    &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;14] us-east1      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;15] us-east4      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;16] us-west2      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;17] us-west3      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;18] us-west4      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;19] cancel
Please enter your numeric choice:  7

Creating App Engine application &lt;span class="k"&gt;in &lt;/span&gt;project nuxt-static-deploy and region &lt;span class="o"&gt;[&lt;/span&gt;
europe-west]....done.                                                          
Services to deploy:

descriptor:      &lt;span class="o"&gt;[&lt;/span&gt;.../nuxt-static-deploy-appengine/dist/app.yaml]
&lt;span class="nb"&gt;source&lt;/span&gt;:          &lt;span class="o"&gt;[&lt;/span&gt;.../nuxt-static-deploy-appengine/dist]
target project:  &lt;span class="o"&gt;[&lt;/span&gt;nuxt-static-deploy]
target service:  &lt;span class="o"&gt;[&lt;/span&gt;default]
target version:  &lt;span class="o"&gt;[&lt;/span&gt;20200602t094056]
target url:      &lt;span class="o"&gt;[&lt;/span&gt;https://20200602t094056-dot-nuxt-static-deploy.ew.r.appspot.com]


     &lt;span class="o"&gt;(&lt;/span&gt;add &lt;span class="nt"&gt;--promote&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;you also want to make this service available from
     &lt;span class="o"&gt;[&lt;/span&gt;https://nuxt-static-deploy.ew.r.appspot.com]&lt;span class="o"&gt;)&lt;/span&gt;

Do you want to &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Y/n&lt;span class="o"&gt;)&lt;/span&gt;?   

Beginning deployment of service &lt;span class="o"&gt;[&lt;/span&gt;default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 12 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Updating service &lt;span class="o"&gt;[&lt;/span&gt;default]...done.                                             
Deployed service &lt;span class="o"&gt;[&lt;/span&gt;default] to &lt;span class="o"&gt;[&lt;/span&gt;https://20200602t094056-dot-nuxt-static-deploy.ew.r.appspot.com]

You can stream logs from the &lt;span class="nb"&gt;command &lt;/span&gt;line by running:
  &lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app logs &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; default

To view your application &lt;span class="k"&gt;in &lt;/span&gt;the web browser run:
  &lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app browse

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🎉 Enjoy
&lt;/h1&gt;

&lt;p&gt;Go &lt;a href="https://nuxt-static-deploy.appspot.com/" rel="noopener noreferrer"&gt;here at https://nuxt-static-deploy.appspot.com/&lt;/a&gt; to see our first Nuxt.js app deployed with AppEngine.&lt;/p&gt;

&lt;h1&gt;
  
  
  🔍 Conclusion
&lt;/h1&gt;

&lt;p&gt;Nuxt.js is a cool technology to create Vue.js app and is trending with the release of &lt;code&gt;@nuxt/content&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1263795755954909184-839" src="https://platform.twitter.com/embed/Tweet.html?id=1263795755954909184"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1263795755954909184-839');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1263795755954909184&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;AppEngine comes with a lot of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Multiple versions available to do A/B testing or Traffic Splitting (each version has a different timestamp on the &lt;a href="https://console.cloud.google.com/appengine/versions" rel="noopener noreferrer"&gt;Versions Page on Appengine&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;🔐 &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/mapping-custom-domains" rel="noopener noreferrer"&gt;Custom Domain Mapping&lt;/a&gt; &amp;amp; auto-renew, free, &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/securing-custom-domains-with-ssl" rel="noopener noreferrer"&gt;managed SSL certificates&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💸 Free quota (1 GB bandwidth)&lt;/li&gt;
&lt;li&gt;⭐️ No instance used with Python37 runtime and our configuration because we have only static content &lt;/li&gt;
&lt;li&gt;🌐 Static content available through Google CDN and &lt;a href="https://peering.google.com/#/infrastructure" rel="noopener noreferrer"&gt;its Edge Network&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⚡️ Managed network: Surprise 🎉 our project uses the http3 procotol &lt;code&gt;h3-Q050&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&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%2Fi%2Fm0egf2fxzh3xsxcvp46g.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%2Fi%2Fm0egf2fxzh3xsxcvp46g.png" alt="Alt Text" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to contact me on &lt;a href="https://twitter.com/jlandure" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; if you have questions!&lt;/p&gt;

&lt;p&gt;NB: You can find another guide on Nuxt.js with AppEngine &lt;a href="https://nuxtjs.org/faq/appengine-deployment" rel="noopener noreferrer"&gt;here in the official documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>googlecloud</category>
      <category>serverless</category>
      <category>vue</category>
    </item>
    <item>
      <title>Crafting the perfect container to play with a Headless Chrome</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 26 May 2020 06:19:17 +0000</pubDate>
      <link>https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp</link>
      <guid>https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp</guid>
      <description>&lt;p&gt;Since July 30th 2017, I have been contributing to a side project: the idea from the beginning was the ability to use Chrome to make some tests in headless mode and avoid a long queue of CI builds using a &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;tiny Docker image&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🤔 Why use a Headless Chrome
&lt;/h1&gt;

&lt;p&gt;In the world of webdev, the ability to run quickly end-to-end tests are important. Popular technologies like Puppeteer enable developers to make fun things like testing, automating forms, crawling, generating screenshots, capturing timeline... And there is a secret: some of these features are directly available on Chrome! 🙌&lt;/p&gt;

&lt;p&gt;For more information on why headless mode is a cool feature, please read a good example on what you can achieve here:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/reverentgeek" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F3194%2Feaf08445-1922-4191-bb07-8b5a3cd4657f.png" alt="reverentgeek"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/reverentgeek/convert-text-html-to-pdf-with-node-js-and-puppeteer-4c53" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Create PDFs with Node.js and Puppeteer&lt;/h2&gt;
      &lt;h3&gt;David Neal ・ Feb 6 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  💡 Crafting the perfect container
&lt;/h1&gt;

&lt;p&gt;With 28 contributors, I have been working on this project to create the perfect image to run Chromium in Headless mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 Tiniest Headless Chrome (&lt;a href="https://github.com/Zenika/alpine-chrome#image-disk-size" rel="noopener noreferrer"&gt;393 MB&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;🐳 Easy to use, ephemeral and reproducible Headless Chrome with Docker&lt;/li&gt;
&lt;li&gt;📝 Doc-friendly with examples for printing the DOM, generating an image with a mobile ratio or generating a PDF.&lt;/li&gt;
&lt;li&gt;👷‍♂️ Autobuild with the &lt;a href="https://hub.docker.com/repository/docker/zenika/alpine-chrome" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt; to sync the project and ship the images with confidence&lt;/li&gt;
&lt;li&gt;📌 Up-to-date with Chromium 81 (81.0.4044.138) and tags available to test different versions of Chromium&lt;/li&gt;
&lt;li&gt;🔐 Secure with the best way to use Chrome and Docker - &lt;a href="https://github.com/Zenika/alpine-chrome#3-ways-to-securely-use-chrome-headless-with-this-image" rel="noopener noreferrer"&gt;See "3 ways to securely use Chrome Headless" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 Ready for internationalisation use: support for asian characters - &lt;a href="https://github.com/Zenika/alpine-chrome/blob/master/with-puppeteer/src/screenshot-asia.js" rel="noopener noreferrer"&gt;See "screenshot-asia.js" file&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💄 Ready for design use: support for WebGL - &lt;a href="https://github.com/Zenika/alpine-chrome#how-to-use-with-webgl" rel="noopener noreferrer"&gt;See "How to use with WebGL" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 Open Source with an Apache2 licence&lt;/li&gt;
&lt;li&gt;👥 Community-built with 28 contributors - &lt;a href="https://github.com/Zenika/alpine-chrome#-contributors" rel="noopener noreferrer"&gt;See "✨ Contributors" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💚 Dev-friendly with examples using NodeJS, Puppeteer, docker-compose and also a test with a X11 display - &lt;a href="https://github.com/Zenika/alpine-chrome#run-examples" rel="noopener noreferrer"&gt;See "Run examples" section&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  💚 Open Source &amp;amp; Fun
&lt;/h1&gt;

&lt;p&gt;Some open source &amp;amp; fun projects have been set up and used for this open source side project. I want to share it with you: it adds value and confidence to maintain a GitHub repository. 👌 &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updates to follow the &lt;a href="https://opensource.guide/" rel="noopener noreferrer"&gt;recommended community standards&lt;/a&gt; from the GitHub community profile&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitmoji.carloscuesta.me/" rel="noopener noreferrer"&gt;Gitmoji&lt;/a&gt; for commit messages &lt;em&gt;where I have contributed in the past&lt;/em&gt; 😊&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://allcontributors.org/" rel="noopener noreferrer"&gt;All Contributors&lt;/a&gt; to thanks everyone who contributes to the project&lt;/li&gt;
&lt;li&gt;Inspiration from &lt;a href="https://github.com/kefranabg/readme-md-generator" rel="noopener noreferrer"&gt;readme-md-generator&lt;/a&gt; made by my friend &lt;a href="https://dev.to/kefranabg"&gt;Franck Abgrall&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inspiration from this cheatsheet on &lt;a href="https://dev.to/kefranabg/cheat-sheet-how-to-promote-your-open-source-project-4flh"&gt;"How to promote your open-source project?"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://renovate.whitesourcesoftware.com/" rel="noopener noreferrer"&gt;Renovate&lt;/a&gt; for dependency updates&lt;/li&gt;
&lt;li&gt;Many reads on dev.to to know what web developers are doing with Chrome and Puppeteer&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  🚀 Reaching a point
&lt;/h1&gt;

&lt;p&gt;Here is the most interesting insight for a tool: to be used and useful! In almost two weeks, this image will be pulled on the &lt;a href="https://hub.docker.com/repository/docker/zenika/alpine-chrome" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;... &lt;/p&gt;

&lt;p&gt;🎉 &lt;strong&gt;a million times&lt;/strong&gt;! 🎉 &lt;/p&gt;

&lt;p&gt;&lt;em&gt;there is currently ~922k downloads and it grows ~6k by day&lt;/em&gt;&lt;br&gt;
On &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, the repository gets ~3000 views, ~1000 unique visitors every two weeks and ~464 stars.&lt;/p&gt;

&lt;p&gt;After 3 years maintaining it, I cannot thank everyone using this container. This is the first time a side project has become so popular.&lt;/p&gt;

&lt;h1&gt;
  
  
  🗓 The Future
&lt;/h1&gt;

&lt;p&gt;✨ Here is some features in the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;with-playwright&lt;/code&gt; version with &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; to reuse the installed headless Chromium (&lt;a href="https://github.com/Zenika/alpine-chrome/pull/85" rel="noopener noreferrer"&gt;PR in draft&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with-deno&lt;/code&gt; version with &lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; to create a useful and fun things to do with deno&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with-lighthouse&lt;/code&gt; version with &lt;a href="https://developers.google.com/web/tools/lighthouse/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; to be able to monitor your website performance in CI builds&lt;/li&gt;
&lt;li&gt;Create a dedicated homepage and documentation website: the &lt;a href="https://github.com/Zenika/alpine-chrome/blob/master/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; begins to be complicated to navigate and update&lt;/li&gt;
&lt;li&gt;Create a gallery with the name of the project or product using this container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📣 Please test this container or play with it!&lt;br&gt;
💻 If you are interested to help me maintain this image, we are welcome! &lt;em&gt;There is some "Good first issue" labelled issue on the repository.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: these terms in this post are synonyms: "Chrome" vs "Chromium" and "image" vs "container".&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>docker</category>
      <category>testing</category>
      <category>javascript</category>
    </item>
    <item>
      <title>State of Cloud Communities in France</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 14 Apr 2020 06:52:54 +0000</pubDate>
      <link>https://dev.to/zenika/state-of-cloud-communities-in-france-7ko</link>
      <guid>https://dev.to/zenika/state-of-cloud-communities-in-france-7ko</guid>
      <description>&lt;p&gt;&lt;a href="https://www.linkedin.com/in/karentamrazyan/" rel="noopener noreferrer"&gt;Karen Tamrazyan&lt;/a&gt; wrote a good article that analyzes local communities of 3 major public cloud providers AWS, Azure and Google Cloud in Germany, Austria and Switzerland (German-speaking countries) here:&lt;br&gt;
&lt;a href="https://www.tamrazyan.com/state-of-cloud-communities-in-german-speaking-countries/" rel="noopener noreferrer"&gt;https://www.tamrazyan.com/state-of-cloud-communities-in-german-speaking-countries/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;His article made me think about France. He helped me to create a similar sheet to superficially compare regional communities behind the three major public cloud providers: Amazon Web Services (aka AWS), Microsoft Azure and Google Cloud.&lt;/p&gt;
&lt;h1&gt;
  
  
  Comparison
&lt;/h1&gt;

&lt;p&gt;The top right quadrant from the latest Gartner cloud market analysis clearly indicates three global cloud leaders: Amazon Web Services, Microsoft Azure, Google Cloud.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2AHLk4BgFc7-QM6iWx" 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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2AHLk4BgFc7-QM6iWx" alt="quadrant" width="1536" height="1536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How are we going to compare the communities with each other? As Karen did in his article, we will compare them by the amount of registered members or the group size in other words. It is obvious that the group size is just one metric of many, like e.g. the activity rate (how often do events take place), the average number of participants, the date of the last event and so on… The members count should serve as a simplified way of measurement of a group success or importance.&lt;/p&gt;

&lt;p&gt;The internet is big. What kind of groups are we going to count? Indeed, there are cloud communities as Facebook groups, on different messenger networks like e.g. Telegram, independently hosted forums and so on. In this work we will count only the communities, which are hosted on the Meetup.com platform. Meetup is at the moment a de facto most popular community platform in those 3 countries.&lt;/p&gt;

&lt;p&gt;We will only consider active groups. It means that a group should have at least 1 registered past or upcoming event. In the locations where multiple communities dedicated to the same cloud technology are present, only the biggest one will be considered because of the very high probability of members overlap between those groups.&lt;/p&gt;

&lt;p&gt;The article gives only a snapshot of the situation as of April 12, 2020. The author doesn’t intend to update it as the situation changes. It’s a photo, not a video.&lt;/p&gt;
&lt;h1&gt;
  
  
  Cloud communities in France
&lt;/h1&gt;

&lt;p&gt;France has a population of over 67 million people. It is one of the most important technological hubs of Europe (competing with Germany and England). Plenty of universities, industrial and service companies belong to the landscape of most of the cities in this country. Even small places can often have lively user groups here like in Brest, Tours or Cannes.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Analysis of Cloud Impact
&lt;/h1&gt;

&lt;p&gt;As we can clearly see from the table, AWS has far away the biggest communities in France : it demonstrates a tendency to go on this Cloud Provider. This popularity is clearly brought from the location of &lt;a href="https://aws.amazon.com/blogs/aws/now-open-aws-eu-paris-region/" rel="noopener noreferrer"&gt;one datacenter in Paris, France at the end of 2017&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Azure have broad community networks that cover the top 5 most populated cities. Azure is never the biggest community in the city.&lt;/p&gt;

&lt;p&gt;Google Cloud groups (aka “GDG Cloud” chapters) have the weakest cloud community network in France among the 3 contenders. However, one should not forget the much wider network of general Google Developer Groups (abbreviated “GDG”), which also on occasions run Google Cloud themed meetups.&lt;/p&gt;

&lt;p&gt;We can observe that Paris, Lyon and Nantes are the top 3 cities where the aws, azure and google cloud communities can enjoy events and meet together. &lt;/p&gt;

&lt;p&gt;Lyon is quite particular : it’s the only city where Google Cloud has more members than the others Cloud communities.&lt;/p&gt;

&lt;p&gt;This article highlights that cloud communities don’t match the &lt;a href="https://medium.com/frenchtech/bienvenue-aux-communaut%C3%A9s-et-capitales-french-tech-labellis%C3%A9es-le-3-avril-2019-f417539d3bd0" rel="noopener noreferrer"&gt;top “La French Tech” cities&lt;/a&gt;. However, many cloud providers provide Cloud credits for Startups like this &lt;a href="https://cloud.google.com/developers/startups" rel="noopener noreferrer"&gt;GCP initiative&lt;/a&gt;: &lt;a href="https://goo.gl/zY4pH2" rel="noopener noreferrer"&gt;check this page to redeem it&lt;/a&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2A7kDNA0EXz_-qRuUo" 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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2A7kDNA0EXz_-qRuUo" alt="frenchtech" width="1400" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some large cities in the country like Montpellier, Bordeaux or Lille and a couple more, where none of Azure or Google Cloud have community footprints. These are opportunities for the future to grow.&lt;/p&gt;

&lt;h1&gt;
  
  
  What about GDGs?
&lt;/h1&gt;

&lt;p&gt;GDGs, Google Developer Groups, are too general to be studied in this article but GDGs bring together tons of passionate developers in France : 4134 members for GDG Paris, 2360 for GDG Nantes, 2213 for GDG Lille and 2133 for GDG Toulouse. &lt;/p&gt;

&lt;p&gt;These numbers are boosted by the organization of a DevFest each year, a day or 2-days conference dedicated to share about the technology and its impact. And GDGs run some Cloud events sometimes, please check out the &lt;a href="https://developers.google.com/community/gdg/groups" rel="noopener noreferrer"&gt;listing of GDGs here&lt;/a&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%2Fx0i8dd17r1a35ox395i6.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%2Fx0i8dd17r1a35ox395i6.png" alt="gdg-france" width="447" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Disclaimer
&lt;/h1&gt;

&lt;p&gt;Author of this article runs voluntarily the &lt;a href="https://meetup.com/fr-FR/GDG-Nantes" rel="noopener noreferrer"&gt;Google Cloud developer group in Nantes (France)&lt;/a&gt; and also the &lt;a href="https://www.meetup.com/fr-FR/GDG-Cloud-Nantes" rel="noopener noreferrer"&gt;GDG Cloud Nantes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He’s also a &lt;a href="https://developers.google.com/community/experts/directory/profile/profile-julien_landur_C3_A9" rel="noopener noreferrer"&gt;GDE Cloud&lt;/a&gt;, a community reward from Google Cloud. His opinions were intended to be honest and unbiased. Apart from this research, the following official sources were used: &lt;a href="https://aws.amazon.com/developer/community/usergroups/europe/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, Azure &amp;amp; &lt;a href="https://cloud.google.com/community/meetups" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/KarenTamrazyan" rel="noopener noreferrer"&gt;Karen Tamrazyan&lt;/a&gt; &amp;amp; &lt;a href="https://twitter.com/charlyx" rel="noopener noreferrer"&gt;Charles-Henri Guérin&lt;/a&gt; for the review of the article.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>community</category>
      <category>meetup</category>
    </item>
    <item>
      <title>Awesome Remote Work</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 17 Mar 2020 14:45:07 +0000</pubDate>
      <link>https://dev.to/zenika/awesome-remote-work-1cfd</link>
      <guid>https://dev.to/zenika/awesome-remote-work-1cfd</guid>
      <description>&lt;p&gt;I discovered the book &lt;a href="https://basecamp.com/books/remote" rel="noopener noreferrer"&gt;"Remote"&lt;/a&gt; written by &lt;a href="https://twitter.com/dhh" rel="noopener noreferrer"&gt;David Heinemeier Hansson&lt;/a&gt; and &lt;a href="https://twitter.com/jasonfried" rel="noopener noreferrer"&gt;Jason Fried&lt;/a&gt; in 2014 when I joined &lt;a href="https://oss.zenika.com/" rel="noopener noreferrer"&gt;Zenika&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Since then, I love to read books on the basecamp culture like &lt;a href="https://basecamp.com/books/rework" rel="noopener noreferrer"&gt;"Rework"&lt;/a&gt; or &lt;a href="https://basecamp.com/books/calm" rel="noopener noreferrer"&gt;"It Doesn't Have to Be Crazy at Work"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the arrival of &lt;a href="https://en.wikipedia.org/wiki/Coronavirus_disease_2019" rel="noopener noreferrer"&gt;COVID-19&lt;/a&gt;, I decided to create a &lt;a href="https://github.com/zenika-open-source/awesome-remote-work" rel="noopener noreferrer"&gt;list of relevant links&lt;/a&gt; to share the culture from "remote" companies to everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  ☁️ Company Culture
&lt;/h2&gt;

&lt;p&gt;Since the beginning of &lt;a href="http://zenika.com/" rel="noopener noreferrer"&gt;Zenika&lt;/a&gt;, there is a "Cloud-first" culture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gsuite for collaboration&lt;/li&gt;
&lt;li&gt;GitHub for code&lt;/li&gt;
&lt;li&gt;Slack for 1-to-1 communication&lt;/li&gt;
&lt;li&gt;Workplace for streamline communication&lt;/li&gt;
&lt;li&gt;SaaS tools for HR, for Sales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these tools seem to be "normal" to be used inside our company and every employee has the right to be in remote when they wish &lt;em&gt;(and notify your manager)&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏠 The path to become a remote company
&lt;/h2&gt;

&lt;p&gt;We are an IT Services company so we share our people skills to our customers to build and deliver software.&lt;br&gt;
To keep our own culture safe, &lt;a href="https://twitter.com/CallMeGoodWater" rel="noopener noreferrer"&gt;my manager&lt;/a&gt; has found a cool way to make people work all together: everyone on Friday will not stay in the customer office. He encourages employees to stay remote (from our own office or from their home). &lt;/p&gt;

&lt;p&gt;First reaction from our customers: they were not able to work remotely. So we helped some of them to adopt a remote culture with the associated tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⏰ The "COVID-19" effect
&lt;/h2&gt;

&lt;p&gt;With the arrival of the Coronavirus disease, some of them didn't take the right patterns and even worse, a few of them panics.&lt;/p&gt;

&lt;p&gt;To empower the remote culture, I just compiled a list of great articles on "How to remote work?" creating an awesome repository on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1239515937193238528-84" src="https://platform.twitter.com/embed/Tweet.html?id=1239515937193238528"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1239515937193238528-84');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1239515937193238528&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  💚 Open source
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/zenika-open-source/awesome-remote-work" rel="noopener noreferrer"&gt;The repository is open-source&lt;/a&gt; and inspired by the &lt;a href="https://github.com/sindresorhus/awesome" rel="noopener noreferrer"&gt;awesome lists from sindresorhus&lt;/a&gt;.&lt;br&gt;
I've added a specific README for 🇫🇷 french advices and links. &lt;br&gt;
If you wish to contribute for your own country, you're welcome! 🙌&lt;/p&gt;

&lt;p&gt;🚀 Feel free to share your links!&lt;/p&gt;

</description>
      <category>remote</category>
      <category>culture</category>
      <category>dev</category>
      <category>streaming</category>
    </item>
    <item>
      <title>How to backup your Firestore data automatically</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Thu, 01 Aug 2019 16:15:03 +0000</pubDate>
      <link>https://dev.to/zenika/how-to-backup-your-firestore-data-automatically-48em</link>
      <guid>https://dev.to/zenika/how-to-backup-your-firestore-data-automatically-48em</guid>
      <description>&lt;p&gt;With my team, we use a lot of Firebase features like Firestore.&lt;/p&gt;

&lt;p&gt;But there is no simple way to backup the data regularly.&lt;/p&gt;

&lt;p&gt;We created a tiny &lt;a href="https://hub.docker.com/r/zenika/alpine-firestore-backup/" rel="noopener noreferrer"&gt;Docker image&lt;/a&gt; &lt;code&gt;zenika/alpine-firestore-backup&lt;/code&gt; and this simple tutorial to perform backups automatically on the Google Cloud Platform with Serverless services like &lt;code&gt;Cloud Run&lt;/code&gt; and &lt;code&gt;Cloud Scheduler&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step1: Create a bucket on GCP
&lt;/h1&gt;

&lt;p&gt;Create a &lt;a href="https://cloud.google.com/storage/docs/storage-classes" rel="noopener noreferrer"&gt;GCP coldline bucket&lt;/a&gt; and save the name of your bucket.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step2: Create a service account
&lt;/h1&gt;

&lt;p&gt;Create a &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-accounts" rel="noopener noreferrer"&gt;GCP Service account&lt;/a&gt; with the following rights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Owner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Datastore Owner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Datastore Import Export Admin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Storage Admin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, download the &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-account-keys" rel="noopener noreferrer"&gt;JSON private key file&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step3: Create your env variables for Cloud Run
&lt;/h1&gt;

&lt;p&gt;Please fill in the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_PROJECT_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_BUCKET_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_SERVICE_KEY&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the &lt;code&gt;GCLOUD_SERVICE_KEY&lt;/code&gt;, make a base64 encoded string using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;key.json | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step4: Set up Cloud Run
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/run/docs/deploying" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; is a serverless service to automatically serve your containers using http.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Cloud Run service&lt;/code&gt; using the public image &lt;code&gt;gcr.io/zenika-hub/alpine-firestore-backup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Be careful to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose your newly image in &lt;code&gt;latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Choose "Cloud Run (fully managed)" and a location&lt;/li&gt;
&lt;li&gt;Enter a service name&lt;/li&gt;
&lt;li&gt;Select "Allow unauthenticated invocations"&lt;/li&gt;
&lt;li&gt;In the "Show optional settings / Environment variables", set the 3 environment variables seen in the previous section&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can test the service using your browser: &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Save the url created to call your Cloud Run Service.&lt;br&gt;
For example: &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/backup&lt;/code&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%2Fja62bl6ygmompxymp26j.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%2Fja62bl6ygmompxymp26j.png" alt="cloud-run" width="544" height="973"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step5: Launch with Cloud Scheduler
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/scheduler/docs/" rel="noopener noreferrer"&gt;Cloud Scheduler&lt;/a&gt; allow you to schedule a cronjob in order to call a https endpoint at regular intervals.&lt;/p&gt;

&lt;p&gt;Prepare a &lt;code&gt;Cloud Scheduler&lt;/code&gt; to send a request to your &lt;code&gt;Cloud Run Service&lt;/code&gt; every time you need.&lt;/p&gt;

&lt;p&gt;For example, every Monday at 3:00am &lt;code&gt;0 3 * * 1&lt;/code&gt; a backup will be done and stored in your bucket.&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%2F2q4j84uwyuja7nk5dw37.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%2F2q4j84uwyuja7nk5dw37.png" alt="cloud-scheduler" width="510" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step6: Monitor the backup operations
&lt;/h1&gt;

&lt;p&gt;You can also check the current status of each backup operation using the following url &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/list&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Feel free to have a look at the Docker image we created for this operation &lt;a href="https://github.com/Zenika/alpine-firestore-backup" rel="noopener noreferrer"&gt;here on github&lt;/a&gt; or &lt;a href="https://hub.docker.com/r/zenika/alpine-firestore-backup/" rel="noopener noreferrer"&gt;here on dockerhub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also have others images we maintained like a popular and small &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;Chromium Headless image&lt;/a&gt; called &lt;code&gt;zenika/alpine-chrome&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
