<?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: Pratham Chauhan</title>
    <description>The latest articles on DEV Community by Pratham Chauhan (@prathamchauhan).</description>
    <link>https://dev.to/prathamchauhan</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%2F4009350%2F278fe6e2-9e2a-4090-b517-ec6f4df96570.png</url>
      <title>DEV Community: Pratham Chauhan</title>
      <link>https://dev.to/prathamchauhan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prathamchauhan"/>
    <language>en</language>
    <item>
      <title>Setting Up Gemini on Vertex AI for Production: A No-Nonsense Walkthrough</title>
      <dc:creator>Pratham Chauhan</dc:creator>
      <pubDate>Tue, 30 Jun 2026 09:51:39 +0000</pubDate>
      <link>https://dev.to/prathamchauhan/setting-up-gemini-on-vertex-ai-for-production-a-no-nonsense-walkthrough-22hp</link>
      <guid>https://dev.to/prathamchauhan/setting-up-gemini-on-vertex-ai-for-production-a-no-nonsense-walkthrough-22hp</guid>
      <description>&lt;p&gt;If you want to call Gemini through Google Cloud's Vertex AI from a real production environment, not just a local script, there's a specific order of operations that saves you from a lot of pain later. This is that walkthrough: project setup, a properly scoped service account, local testing, and finally getting it running securely on an EC2 instance, including the keyless Workload Identity Federation (WIF) path.&lt;/p&gt;

&lt;p&gt;You might be wondering why bother with all this instead of just grabbing an API key from AI Studio and dropping it into an environment variable. Honestly, for a weekend project, do that. But a raw API key is a single static string that grants full access to whatever it's scoped to, forever, until you remember to revoke it. If it ends up in a public repo, a client side bundle, or a log file, anyone holding it can run up your bill or worse. The setup in this post trades a bit of upfront complexity for something much safer: identity based access that can be scoped down to exactly one permission, rotated without touching your app code, and in the WIF case, never even exists as a file you could accidentally leak. It's the difference between handing someone a house key and giving them a temporary badge that only opens one door and expires on its own.&lt;/p&gt;

&lt;p&gt;Before diving in, a couple of terms that will come up a lot. An IAM role is just a labeled bundle of permissions, like a job title that comes with a fixed list of things you're allowed to do. A service account is not a human user. Think of it as a robot identity that your app logs in as, instead of a person typing a password. We'll use both throughout.&lt;/p&gt;

&lt;p&gt;Part 2 covers everything that goes wrong if you skip a step or fat finger a detail here, and trust me, there's a lot that can go wrong. But first, let's do it right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Pick your project and enable the right API
&lt;/h2&gt;

&lt;p&gt;Open the Google Cloud Console and confirm you're in the correct project (not just "a" project, since billing and permissions are scoped per project, and it's easy to set things up in the wrong one if you have several).&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;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-gcp-project-id"&lt;/span&gt;
&lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API you want is &lt;code&gt;aiplatform.googleapis.com&lt;/code&gt;, labeled "Vertex AI API" in the console. An API here just means a specific Google Cloud service you have to switch on before you can use it. There's a deceptively similar sounding one called Vertex AI Search for commerce (&lt;code&gt;retail.googleapis.com&lt;/code&gt;) that has nothing to do with calling Gemini models. Enable the right one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;aiplatform.googleapis.com
gcloud services list &lt;span class="nt"&gt;--enabled&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aiplatform.googleapis.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vertex AI locations aren't inherited from your project. You choose a region explicitly, meaning the physical data center area where your requests get processed. &lt;code&gt;us-central1&lt;/code&gt; is a safe, well supported default. Whatever you pick, use the same value everywhere: local testing, EC2 env vars, your app code. Mismatched regions are a surprisingly common source of "it works locally but not in prod."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Set a budget before you test anything
&lt;/h2&gt;

&lt;p&gt;Before your first API call, set up a budget alert. Go to Billing, then Budgets &amp;amp; alerts, then Create budget. Scope it to this specific project, and set a monthly amount you're comfortable with. A hundred dollars is a reasonable starting point. Add alert thresholds at every 10% so you get early warning, not just a surprise at the end of the month.&lt;/p&gt;

&lt;p&gt;One important caveat: budgets are alerts, not hard stops. Google Cloud won't automatically cut you off at your limit. If you need an actual ceiling, build it into your own backend. Check estimated monthly spend before each Gemini call and reject the request if you're over:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;monthlySpendUsd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&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;Monthly Gemini budget reached&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;This is more reliable than a billing disable webhook, because it stops the request, not the project, which matters if this project hosts anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create a least privilege service account
&lt;/h2&gt;

&lt;p&gt;"Least privilege" just means giving something only the exact permissions it needs to do its job, nothing extra "just in case." Resist the urge to reuse an existing service account or grant Editor or Owner, which are broad, all access roles. Create a fresh service account dedicated to this one purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;amp; Admin → Service Accounts → Create service account
Name: gemini-prod-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then grant it exactly one role, &lt;code&gt;roles/aiplatform.user&lt;/code&gt; (shown as "Vertex AI User" in the console). This single role covers everything needed to call Gemini models. It doesn't need Owner, Editor, or any billing or admin permissions, so even if this identity were somehow compromised, the blast radius is small: someone could call Gemini on your dime, but they couldn't touch your other cloud resources or billing settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud projects add-iam-policy-binding &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"serviceAccount:gemini-prod-runner@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.iam.gserviceaccount.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/aiplatform.user"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Test locally before touching any servers
&lt;/h2&gt;

&lt;p&gt;For local development, Application Default Credentials, usually shortened to ADC, are the path of least resistance. ADC is just Google's term for "let the command line tool log you in once, then every script on this machine can quietly reuse that login" instead of you managing key files by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth application-default login
gcloud auth application-default set-quota-project &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That second command matters more than it looks. It tells Google which project to bill your test requests against. Skip it and you'll get vague "quota exceeded" or "API not enabled" errors that have nothing to do with quotas or the API.&lt;/p&gt;

&lt;p&gt;Set your environment:&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;export &lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a minimal test script:&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;GoogleGenAI&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;@google/genai&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;ai&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;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;vertexai&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;project&lt;/span&gt;&lt;span class="p"&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;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&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;GOOGLE_CLOUD_LOCATION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-central1&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;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Say hello in one sentence.&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&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;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather test with the exact kind of credentials you'll use in production, generate a service account key, which is just a downloadable JSON file containing a long lived password for that robot identity. This is fine for local testing only (more on why not to ship this to prod below):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts keys create ~/secrets/gemini-prod-runner.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iam-account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemini-prod-runner@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.iam.gserviceaccount.com
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/secrets/gemini-prod-runner.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/secrets/gemini-prod-runner.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;secrets/&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, and anything matching &lt;code&gt;*credentials*&lt;/code&gt; or &lt;code&gt;*service-account*&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; immediately. Don't wait until after the first commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Deploying to EC2, two paths
&lt;/h2&gt;

&lt;p&gt;You have two reasonable options here, and which one you pick is a real tradeoff, not just a "more secure equals always better" decision.&lt;/p&gt;

&lt;p&gt;Option A is putting that service account JSON key file on the server's disk. Copy the key file to the instance, point &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; at it, and you're done. Fast to set up, easy to debug, and perfectly fine for getting a first deployment working. The tradeoff is that it's a long lived static secret sitting on a real machine. If it leaks, it's valid until you manually revoke it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp ~/secrets/gemini-prod-runner.json ubuntu@&amp;lt;EC2_IP&amp;gt;:/home/ubuntu/gemini-prod-runner.json
ssh ubuntu@&amp;lt;EC2_IP&amp;gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;600 /home/ubuntu/gemini-prod-runner.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Option B is Workload Identity Federation, or WIF. In plain terms, WIF lets two clouds vouch for each other without ever sharing a password. AWS already knows, with certainty, which EC2 instance is making a request. WIF lets Google trust that AWS issued vouching instead of asking for a Google specific secret. No Google key ever touches the instance. Instead, AWS proves the instance's identity, and Google exchanges that proof for a short lived token that expires on its own, on demand, every time. More setup work, but nothing sitting on disk to rotate or leak.&lt;/p&gt;

&lt;p&gt;If this is your first time deploying this app, I'd genuinely recommend starting with Option A, confirming everything else works end to end, and then swapping to WIF as an isolated second step. Debugging two unfamiliar systems at once, your app's Vertex AI integration and a federated trust chain between two clouds, is much harder than debugging them one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Setting up WIF properly
&lt;/h2&gt;

&lt;p&gt;This is the part with the most moving pieces, so go slowly, and here are the building blocks in plain language before the steps.&lt;/p&gt;

&lt;p&gt;An IAM role on the AWS side (yes, AWS has its own separate concept also called a role, easy to confuse with Google's IAM role) is what gives your EC2 instance a verifiable identity card. It's not about granting AWS permissions here, it's purely "this machine is who it says it is."&lt;/p&gt;

&lt;p&gt;A Workload Identity Pool on the Google side is basically a waiting room where Google agrees to listen to identities from somewhere outside Google, like AWS. A provider inside that pool is the specific configuration that says "and here's exactly how to verify an AWS identity, and here's which AWS account I trust." A principal is Google's general word for "the identity asking for access," whether that's a person, a service account, or in this case, an AWS role being recognized through the pool.&lt;/p&gt;

&lt;p&gt;On the AWS side, create that IAM role for EC2. It doesn't need any special AWS permissions, since it exists purely to give the instance a verifiable identity, not to grant AWS side access to anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM → Roles → Create role → Trusted entity: AWS service → Use case: EC2
Name: ec2-gemini-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach it to your running instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EC2 → Instances → select instance → Actions → Security → Modify IAM role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Google side, create the Workload Identity Pool and an AWS provider inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;amp; Admin → Workload Identity Federation → Create pool
Pool ID: aws-ec2-pool
Provider type: AWS
Provider ID: aws-ec2-provider
AWS account ID: &amp;lt;your AWS account ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then grant that pool's identity permission to act as your service account, or grant the role directly to the AWS identity as its own principal. Google supports both patterns, and which one your setup uses depends on how you configure the binding (a binding is just the rule that says "this identity gets this permission"). The console's "Connected service accounts" flow under your pool will show you which path you're on. Either way, the end result needs to be that this specific AWS role can obtain a Google access token scoped to call Vertex AI.&lt;/p&gt;

&lt;p&gt;Download the generated credential configuration file and place it on the instance:&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;export &lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/ubuntu/gcp-wif-credential.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is not a private key. It's a small JSON document of instructions telling Google's auth library how to go ask AWS who it's talking to. Worth opening it once and reading it, since knowing its shape will save you real time if something goes wrong later (Part 2 has a lot more on this).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Wire it into your process manager
&lt;/h2&gt;

&lt;p&gt;Whatever you use to run the app in production, PM2, systemd, Docker, make sure these environment variables actually reach the running process, not just your shell:&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;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-project-id
&lt;span class="nv"&gt;GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-central1
&lt;span class="nv"&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/ubuntu/gcp-wif-credential.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A systemd unit, for reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;My Gemini Vertex AI App&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ubuntu&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/my-app&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CLOUD_PROJECT=your-project-id"&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CLOUD_LOCATION=us-central1"&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_GENAI_USE_VERTEXAI=true"&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_APPLICATION_CREDENTIALS=/home/ubuntu/gcp-wif-credential.json"&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/npm start&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pre-production checklist
&lt;/h2&gt;

&lt;p&gt;A quick list worth running through before calling this done. The correct API, &lt;code&gt;aiplatform.googleapis.com&lt;/code&gt; and not &lt;code&gt;retail.googleapis.com&lt;/code&gt;, is enabled. A budget with staged alerts exists. The service account has &lt;code&gt;roles/aiplatform.user&lt;/code&gt; only, nothing broader. Any key files are outside the repo, &lt;code&gt;chmod 600&lt;/code&gt;, and &lt;code&gt;.gitignore&lt;/code&gt;'d. On EC2, you've picked one auth path deliberately rather than half configuring both. And your application has its own spend guard rather than relying solely on Google's billing alerts.&lt;/p&gt;

&lt;p&gt;That's the setup. If everything above goes exactly to plan, you're done. If it doesn't, and there are a surprising number of ways it doesn't, Part 2 walks through every failure mode I personally hit, in the order I hit them, and how to actually diagnose each one instead of guessing.&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>google</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
