<?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: Phat Tran</title>
    <description>The latest articles on DEV Community by Phat Tran (@phattran20).</description>
    <link>https://dev.to/phattran20</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%2F4001860%2Fe877dd54-ffe1-48fc-9179-279c00adc245.png</url>
      <title>DEV Community: Phat Tran</title>
      <link>https://dev.to/phattran20</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phattran20"/>
    <language>en</language>
    <item>
      <title>How far does PocketBase Cloud's free tier actually go? I built something using only the free plan</title>
      <dc:creator>Phat Tran</dc:creator>
      <pubDate>Tue, 21 Jul 2026 17:09:12 +0000</pubDate>
      <link>https://dev.to/phattran20/how-far-does-pocketbase-clouds-free-tier-actually-go-i-built-something-using-only-the-free-plan-2m0p</link>
      <guid>https://dev.to/phattran20/how-far-does-pocketbase-clouds-free-tier-actually-go-i-built-something-using-only-the-free-plan-2m0p</guid>
      <description>&lt;p&gt;Free tiers on hosting platforms usually share the same pattern: fine for a demo, but the moment you actually try to use one you hit a wall. This post isn't just talk — I built a small real project using nothing but PocketBase Cloud's free plan, start to finish, to see how far it actually goes.&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%2Fny5dwjm9tnalon9zxmu2.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%2Fny5dwjm9tnalon9zxmu2.png" alt="Pocketbasecloud" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the free plan
&lt;/h2&gt;

&lt;p&gt;PocketBase Cloud's free plan includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 PocketBase instance (50MB disk limit)&lt;/li&gt;
&lt;li&gt;5 frontend deployments (no custom domains on free — that starts at the Starter plan)&lt;/li&gt;
&lt;li&gt;Runs on a dedicated server cluster reserved for free plan users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing people often ask: why does a free plan still need a Stripe card? It's a $0 subscription used purely for verification and abuse prevention (stopping people from spamming hundreds of instances) — you're never actually charged.&lt;/p&gt;

&lt;p&gt;No rate limits like PocketHost enforces, and no hibernation — the free instance runs 24/7 with no cold starts. That's the biggest difference from most other free tiers out there, which tend to hibernate idle instances to cut costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo project: a mini link shortener
&lt;/h2&gt;

&lt;p&gt;To test this properly, I built a small link shortener — a mini bit.ly — using only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 PocketBase instance (free) for the database + API + auth for the admin page&lt;/li&gt;
&lt;li&gt;1 frontend (free) — a simple React app, deployed as a static site&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Schema
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;links&lt;/code&gt; collection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;slug&lt;/code&gt; (text, unique)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;target_url&lt;/code&gt; (url)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clicks&lt;/code&gt; (number, default 0)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;owner&lt;/code&gt; (relation to users, optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create rule: &lt;code&gt;@request.auth.id != ""&lt;/code&gt; — only logged-in users can create links. List/view rules are left public, since a link shortener needs to redirect for anyone, logged in or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redirect logic via a hook
&lt;/h3&gt;

&lt;p&gt;This is where hooks (PocketBase's embedded JS VM) earn their keep — no separate backend needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;routerAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/r/{slug}&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pathValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slug&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;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findFirstRecordByFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;links&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`slug = "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Link not found&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;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clicks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clicks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target_url&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 route runs right inside the instance — no extra backend service to deploy. Backend hosting (Node/Deno/Bun) is Pro-plan-only anyway, so hooks are exactly how you get custom logic on the free plan without paying for anything extra.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;A small React page: a form to create links, a list showing click counts. No custom domain (not available on free), so it runs on the default subdomain you get assigned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where you actually hit the limits
&lt;/h2&gt;

&lt;p&gt;After building it and stress-testing with some sample data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;50MB disk&lt;/strong&gt;: with a schema that's mostly text/number fields, you can fit tens of thousands of records before hitting the ceiling — plenty for a demo, an MVP, or a genuinely small personal project. If you're storing files (images, video), 50MB disappears fast, and the free plan won't be enough for anything file-heavy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 instance&lt;/strong&gt;: if you need separate staging/production databases, or a multi-tenant setup with one instance per client, the free plan won't cut it — that's when it's time to look at the Starter plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No custom domain&lt;/strong&gt;: fine for an internal MVP or a demo you send to a client, but a real product launching publicly will almost certainly need its own domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who this free tier actually makes sense for
&lt;/h2&gt;

&lt;p&gt;The free plan isn't meant for serious production traffic — that's true of pretty much every free tier out there. But unlike many free tiers that hibernate instances or throttle requests to the point of being barely usable, here you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A genuinely running instance, 24/7, no cold starts, so you can evaluate the real experience before paying anything&lt;/li&gt;
&lt;li&gt;Enough to demo to a client, run a personal side project, or just learn PocketBase without spinning up your own VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're on the fence about trying PocketBase Cloud, the free plan is the cheapest way to find out for yourself — no real charge on your card, and building something small like this takes about 15–20 minutes to know whether it fits what you need.&lt;/p&gt;

&lt;p&gt;Try it at &lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;pocketbasecloud.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Have you built anything on a hosting platform's free tier before — did it actually hold up, or was it just a demo before the upgrade prompt?&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>hosting</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>PocketBase Cloud vs PocketHost — which one should you deploy PocketBase on?</title>
      <dc:creator>Phat Tran</dc:creator>
      <pubDate>Tue, 21 Jul 2026 16:49:46 +0000</pubDate>
      <link>https://dev.to/phattran20/pocketbase-cloud-vs-pockethost-which-one-should-you-deploy-pocketbase-on-21nc</link>
      <guid>https://dev.to/phattran20/pocketbase-cloud-vs-pockethost-which-one-should-you-deploy-pocketbase-on-21nc</guid>
      <description>&lt;p&gt;If you're running PocketBase and looking for managed hosting instead of babysitting your own VPS, you've probably run into two names: &lt;strong&gt;PocketHost&lt;/strong&gt; and &lt;strong&gt;PocketBase Cloud&lt;/strong&gt;. Both let you deploy PocketBase in minutes with zero Docker or Nginx config. But under the "managed hosting" label, they're solving pretty different problems.&lt;/p&gt;

&lt;p&gt;Full disclosure: I built PocketBase Cloud, so this isn't 100% neutral — but I'll stick to concrete numbers so you can judge for yourself.&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%2F6y6f4eooy2zpjadrytxa.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%2F6y6f4eooy2zpjadrytxa.png" alt="PocketBase Cloud and PocketHost comparison showing production infrastructure, monitoring, rate limits, and instance hibernation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limits — the biggest difference
&lt;/h2&gt;

&lt;p&gt;PocketHost enforces several layers of rate limiting at the Cloudflare edge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50 requests / 10 seconds / IP&lt;/li&gt;
&lt;li&gt;1,000 requests / hour / IP&lt;/li&gt;
&lt;li&gt;10,000 requests / hour / instance&lt;/li&gt;
&lt;li&gt;5 concurrent requests / IP&lt;/li&gt;
&lt;li&gt;50 concurrent requests / instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's fine for a side project, but if you're running SSR (Next.js, Nuxt, etc.) or a backend proxy that talks to PocketBase from a single server, all your traffic comes from &lt;strong&gt;one IP&lt;/strong&gt;. That "5 concurrent requests per IP" cap suddenly becomes a real bottleneck, not a theoretical one.&lt;/p&gt;

&lt;p&gt;PocketBase Cloud doesn't enforce per-IP or per-instance caps. Throughput depends entirely on the server resources you're on — a Pro plan instance with 2 vCPU / 4GB RAM can handle 1,000+ requests per second with no artificial ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hibernation — does your cron job actually fire on time?
&lt;/h2&gt;

&lt;p&gt;This is the one people usually don't notice until it bites them. PocketHost instances can hibernate during periods of inactivity. When that happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cron jobs and scheduled tasks may fail to run&lt;/li&gt;
&lt;li&gt;The first request after waking up can be delayed or fail outright&lt;/li&gt;
&lt;li&gt;Missed cron intervals are &lt;strong&gt;not replayed&lt;/strong&gt; after wake-up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an app with steady traffic this barely matters. But for a low-traffic B2B app, or anything relying on cron for emails, syncing, billing, etc., hibernation is a quiet risk sitting in the background.&lt;/p&gt;

&lt;p&gt;PocketBase Cloud runs 24/7 — no hibernation, no cold starts. It behaves exactly like PocketBase on your own server, except we handle the infra, monitoring, and security hardening for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not just PocketBase hosting
&lt;/h2&gt;

&lt;p&gt;This is more of a product philosophy difference than a feature checklist. PocketHost is single-purpose: it only hosts PocketBase. PocketBase Cloud is a full application platform — within the same project you can deploy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PocketBase&lt;/strong&gt; — database, auth, realtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend services&lt;/strong&gt; (Node.js, Deno, Bun) — for logic that goes beyond what PocketBase hooks can do, or when you need something from the JS ecosystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static sites / frontends&lt;/strong&gt; — React, Vue, Svelte, Astro, etc., with custom domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything in the project shares the same infrastructure and can talk to each other directly — no CORS workarounds, no billing scattered across three different providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PocketBase Cloud&lt;/th&gt;
&lt;th&gt;PocketHost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request rate limits&lt;/td&gt;
&lt;td&gt;None (based on server resources)&lt;/td&gt;
&lt;td&gt;Per-IP / per-instance caps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hibernation&lt;/td&gt;
&lt;td&gt;No — 24/7 uptime&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free plan&lt;/td&gt;
&lt;td&gt;1 PocketBase + 5 frontends&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static site hosting&lt;/td&gt;
&lt;td&gt;Yes, custom domains&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend hosting (Node/Deno/Bun)&lt;/td&gt;
&lt;td&gt;Yes (Pro plan)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-service in one project&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;PocketBase only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom domains&lt;/td&gt;
&lt;td&gt;Starter plan and up&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time log streaming&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server monitoring (CPU/RAM/disk)&lt;/td&gt;
&lt;td&gt;Yes (Pro plan)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  So which one should you pick?
&lt;/h2&gt;

&lt;p&gt;If you're running a small side project with low traffic and don't care about cold starts, PocketHost is a fine, simple choice.&lt;/p&gt;

&lt;p&gt;If you need real production reliability, have SSR/proxy traffic funneling through one IP, rely on cron jobs, or want your frontend + backend + database in one place instead of juggling three providers — that's exactly the gap PocketBase Cloud was built to fill.&lt;/p&gt;

&lt;p&gt;PocketBase Cloud has a free plan (1 PocketBase instance + 5 frontend deployments), no credit card required to actually try it — check it out at &lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;pocketbasecloud.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Where are you hosting PocketBase right now — self-hosted, PocketHost, Coolify, something else? Curious to hear how it's going for you 👇&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>hosting</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Edit Hooks and Add Routes on PocketBase Cloud</title>
      <dc:creator>Phat Tran</dc:creator>
      <pubDate>Thu, 16 Jul 2026 08:25:42 +0000</pubDate>
      <link>https://dev.to/phattran20/how-i-edit-hooks-and-add-routes-on-pocketbase-cloud-1o8i</link>
      <guid>https://dev.to/phattran20/how-i-edit-hooks-and-add-routes-on-pocketbase-cloud-1o8i</guid>
      <description>&lt;h2&gt;
  
  
  Extending with Hooks
&lt;/h2&gt;

&lt;p&gt;PocketBase hooks let you run custom JavaScript on the server — right inside your instance. Add API routes, react to record changes, schedule cron jobs, or send emails, all without deploying a separate backend.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt;, you write and manage hooks directly in the dashboard: open your instance and go to the Hooks tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a hook
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your PocketBase instance in the dashboard&lt;/li&gt;
&lt;li&gt;Go to the Hooks tab&lt;/li&gt;
&lt;li&gt;Click New Hook, give the file a name ending in &lt;code&gt;.pb.js&lt;/code&gt; (e.g., &lt;code&gt;main.pb.js&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Write your code and save — the instance reloads hooks automatically
  &lt;iframe src="https://www.youtube.com/embed/qEyuLTdM2fU"&gt;
  &lt;/iframe&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example: a custom API route
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;routerAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/hello/{name}&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pathValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hello &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;!`&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;Your route is immediately available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;instance-name&amp;gt;.pocketbasecloud.com/api/hello/world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: react to record changes
&lt;/h2&gt;

&lt;p&gt;Run logic whenever a record is created, updated, or deleted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onRecordCreate&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// runs before the record is persisted — you can still modify it&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&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="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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;onRecordAfterCreateSuccess&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// runs after the record is saved&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;new post:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: scheduled cron jobs
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;cronAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cleanup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 3 * * *&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="o"&gt;=&amp;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;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findRecordsByFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sessions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;created &amp;lt; @yesterday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &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;record&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&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;h2&gt;
  
  
  Example: send an email
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onRecordAfterCreateSuccess&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&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;MailerMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;senderAddress&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;p&amp;gt;Thanks for signing up.&amp;lt;/p&amp;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;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newMailClient&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What hooks can access
&lt;/h2&gt;

&lt;p&gt;Hooks run in PocketBase’s embedded JavaScript VM with access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$app&lt;/code&gt; — the full PocketBase app instance (query, create, update records)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;routerAdd&lt;/code&gt; — register custom HTTP routes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onRecord*&lt;/code&gt; event handlers — before/after create, update, delete&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cronAdd&lt;/code&gt; — cron-style scheduled jobs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$http.send()&lt;/code&gt; — make outbound HTTP requests to third-party APIs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$os&lt;/code&gt;, &lt;code&gt;$filesystem&lt;/code&gt;, &lt;code&gt;$security&lt;/code&gt; — utility namespaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the PocketBase JSVM documentation for the complete API.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use a backend instead
&lt;/h2&gt;

&lt;p&gt;Hooks are perfect for lightweight logic tied to your data. For heavier workloads — long-running jobs, large dependencies, websockets, or code you want to develop and test locally as a normal project — deploy a dedicated backend alongside your instance.&lt;/p&gt;

&lt;p&gt;You can deploy and manage PocketBase instances, hooks, environment variables, backups, and custom domains from the &lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; dashboard.&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>pocketbasecloud</category>
      <category>ai</category>
      <category>backend</category>
    </item>
    <item>
      <title>How to Use Environment Variables in PocketBase Hooks</title>
      <dc:creator>Phat Tran</dc:creator>
      <pubDate>Fri, 10 Jul 2026 04:20:31 +0000</pubDate>
      <link>https://dev.to/phattran20/how-to-use-environment-variables-in-pocketbase-hooks-3a93</link>
      <guid>https://dev.to/phattran20/how-to-use-environment-variables-in-pocketbase-hooks-3a93</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/3nelo7zAGLk"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
API keys, webhook secrets, and third-party credentials should never be hardcoded&lt;br&gt;
inside your PocketBase hooks.&lt;/p&gt;

&lt;p&gt;Instead of writing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;re_live_your_real_secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can store the value as an environment variable in &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; and read&lt;br&gt;
it only when your server-side hook runs.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add an environment variable from the &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; dashboard;&lt;/li&gt;
&lt;li&gt;access it from a PocketBase JavaScript hook;&lt;/li&gt;
&lt;li&gt;verify that the variable is available without exposing its value; and&lt;/li&gt;
&lt;li&gt;update or remove it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; account;&lt;/li&gt;
&lt;li&gt;a project with a running PocketBase instance; and&lt;/li&gt;
&lt;li&gt;access to the &lt;strong&gt;Hooks&lt;/strong&gt; and &lt;strong&gt;Environment Variables&lt;/strong&gt; pages for that instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need SSH access to the server, and you do not need to upload a&lt;br&gt;
&lt;code&gt;.env&lt;/code&gt; file manually.&lt;/p&gt;
&lt;h2&gt;
  
  
  Open the Environment Variables page
&lt;/h2&gt;

&lt;p&gt;Sign in to the &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; dashboard and follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Projects&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your project.&lt;/li&gt;
&lt;li&gt;Open the PocketBase instance you want to configure.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Environment Variables&lt;/strong&gt; from the instance sidebar.&lt;/li&gt;
&lt;/ol&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%2Fg9ezr4pfl9lrmauzenwz.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%2Fg9ezr4pfl9lrmauzenwz.png" alt="PocketBase Cloud sidebar showing the Environment Variables menu item." width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This page lists the environment variables attached to the selected PocketBase&lt;br&gt;
instance.&lt;/p&gt;
&lt;h2&gt;
  
  
  Add an environment variable
&lt;/h2&gt;

&lt;p&gt;Select &lt;strong&gt;Add Variable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For this tutorial, create the following example variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Key: RESEND_API_KEY
Value: re_your_example_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a fake value while testing. Never include a real API key in screenshots,&lt;br&gt;
public repositories, support messages, or blog posts.&lt;/p&gt;

&lt;p&gt;Environment variable keys must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start with an uppercase letter or underscore;&lt;/li&gt;
&lt;li&gt;contain only uppercase letters, numbers, and underscores; and&lt;/li&gt;
&lt;li&gt;not contain spaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of valid keys include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RESEND_API_KEY
STRIPE_SECRET_KEY
DISCORD_WEBHOOK_URL
INTERNAL_API_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Frhzswlt0457e9qjp79k0.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%2Frhzswlt0457e9qjp79k0.png" alt="Add Environment Variable dialog filled with RESEND_API_KEY and a fake value" width="800" height="763"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Add Variable&lt;/strong&gt; to save it. &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; synchronizes the variable&lt;br&gt;
with the instance and restarts the PocketBase service so that the new value is&lt;br&gt;
available to hooks.&lt;/p&gt;

&lt;p&gt;The restart may take a few seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Read the variable from a PocketBase hook
&lt;/h2&gt;

&lt;p&gt;PocketBase JavaScript hooks do not run in Node.js. They run in PocketBase's&lt;br&gt;
embedded JavaScript runtime, so &lt;code&gt;process.env.RESEND_API_KEY&lt;/code&gt; is not the correct&lt;br&gt;
API here.&lt;/p&gt;

&lt;p&gt;Use PocketBase's &lt;code&gt;$os.getenv()&lt;/code&gt; helper instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$os.getenv()&lt;/code&gt; returns the current value as a string. If the variable does not&lt;br&gt;
exist, it returns an empty string.&lt;/p&gt;

&lt;p&gt;You can find more information in the&lt;br&gt;
&lt;a href="https://pocketbase.io/docs/js-overview/" rel="noopener noreferrer"&gt;PocketBase JavaScript overview&lt;/a&gt; and&lt;br&gt;
the &lt;a href="https://pocketbase.io/jsvm/interfaces/os.getenv.html" rel="noopener noreferrer"&gt;&lt;code&gt;$os.getenv()&lt;/code&gt; reference&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a hook to verify the configuration
&lt;/h2&gt;

&lt;p&gt;Return to your PocketBase instance and open &lt;strong&gt;Hooks&lt;/strong&gt; from the sidebar.&lt;/p&gt;

&lt;p&gt;Create a file named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env-check.pb.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;routerAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/env-check&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;configured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route only reports whether the variable exists. It does not return the&lt;br&gt;
secret itself.&lt;/p&gt;



&lt;p&gt;Save the hook and make sure it is active.&lt;/p&gt;
&lt;h2&gt;
  
  
  Test the hook
&lt;/h2&gt;

&lt;p&gt;Call the new route using your PocketBase instance domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://YOUR_POCKETBASE_DOMAIN/api/env-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the variable is configured, the response will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configured"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response confirms that the hook can access the variable without sending&lt;br&gt;
the secret to the client.&lt;/p&gt;


&lt;h2&gt;
  
  
  Use the variable in a real hook
&lt;/h2&gt;

&lt;p&gt;After confirming that the variable is available, you can use it when calling a&lt;br&gt;
third-party API.&lt;/p&gt;

&lt;p&gt;The following example shows the general pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;routerAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/send-message&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The email service is not configured&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="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="nx"&gt;$http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/v1/messages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from PocketBase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;statusCode&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;json&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;Replace the example URL and request body with the API you are integrating.&lt;/p&gt;

&lt;p&gt;The important part is that the credential comes from &lt;code&gt;$os.getenv()&lt;/code&gt; instead&lt;br&gt;
of appearing in the hook source.&lt;/p&gt;
&lt;h2&gt;
  
  
  Update an environment variable
&lt;/h2&gt;

&lt;p&gt;To rotate a credential or change its value:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return to &lt;strong&gt;Environment Variables&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Add Variable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter the existing key, such as &lt;code&gt;RESEND_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter the new value and save it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Saving an existing key updates its value. After the PocketBase instance&lt;br&gt;
restarts, hooks will read the new value automatically. You do not need to&lt;br&gt;
change the hook source.&lt;/p&gt;
&lt;h2&gt;
  
  
  Delete an environment variable
&lt;/h2&gt;

&lt;p&gt;To remove a variable, select the delete button next to its key and confirm the&lt;br&gt;
action.&lt;/p&gt;

&lt;p&gt;Your hook should always handle a missing variable safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The email service is not configured&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;This produces a controlled error instead of allowing the hook to make an&lt;br&gt;
invalid request with an empty credential.&lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The endpoint returns &lt;code&gt;configured: false&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The key in the dashboard is exactly &lt;code&gt;RESEND_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The key passed to &lt;code&gt;$os.getenv()&lt;/code&gt; uses the same spelling and capitalization.&lt;/li&gt;
&lt;li&gt;The environment variable was saved successfully.&lt;/li&gt;
&lt;li&gt;The PocketBase instance has finished restarting.&lt;/li&gt;
&lt;li&gt;The hook file is saved and active.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;process.env&lt;/code&gt; is undefined
&lt;/h3&gt;

&lt;p&gt;PocketBase hooks do not run in Node.js. Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;YOUR_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The hook worked before the variable was changed
&lt;/h3&gt;

&lt;p&gt;Wait a few seconds for the instance restart to finish, then call the endpoint&lt;br&gt;
again. Also confirm that the variable key was not accidentally changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  You are ready
&lt;/h2&gt;

&lt;p&gt;You can now manage credentials from the &lt;a href="https://pocketbasecloud.com/" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt; dashboard and access&lt;br&gt;
them safely from PocketBase JavaScript hooks using &lt;code&gt;$os.getenv()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This keeps secrets out of your source code and makes credential rotation much&lt;br&gt;
easier: update the value in the dashboard, wait for the instance to restart,&lt;br&gt;
and your existing hooks will use the new credential automatically.&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>How to deploy your first PocketBase instance on PocketBase Cloud</title>
      <dc:creator>Phat Tran</dc:creator>
      <pubDate>Fri, 03 Jul 2026 05:26:20 +0000</pubDate>
      <link>https://dev.to/phattran20/how-to-deploy-your-first-pocketbase-instance-on-pocketbase-cloud-197i</link>
      <guid>https://dev.to/phattran20/how-to-deploy-your-first-pocketbase-instance-on-pocketbase-cloud-197i</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/QNcMGfldgj8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I chose PocketBase Cloud
&lt;/h2&gt;

&lt;p&gt;If you haven't used &lt;a href="https://pocketbase.io" rel="noopener noreferrer"&gt;PocketBase&lt;/a&gt;: it's an open-source backend in a single Go binary. Embedded SQLite, auth, real-time subscriptions, file storage, REST API, admin UI — all included. &lt;br&gt;
The catch has always been hosting it: renting a VPS, configuring a reverse proxy, SSL, backups, monitoring.&lt;/p&gt;

&lt;p&gt;That's exactly the gap &lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;&lt;strong&gt;PocketBase Cloud&lt;/strong&gt;&lt;/a&gt; fills. It's a hosting platform built specifically for PocketBase: you click a button and get a fully managed instance — deployed on real infrastructure, secured with SSL and DDoS protection, backed up, and monitored — live in about 30 seconds, with a free plan to start. Think Vercel, but for your PocketBase backend.&lt;/p&gt;

&lt;p&gt;This post walks through creating your first instance on it, start to finish.&lt;/p&gt;

&lt;p&gt;I looked at a few hosts and went with PocketBase Cloud for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Free plan to test with&lt;/strong&gt; — 1 instance, 50 MB storage, no charge. (Heads-up: it asks for a Stripe subscription at $0 to verify you're human. You don't get billed. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 regions&lt;/strong&gt; — Germany ×2, Finland, US East/West, Singapore. My users are mostly in Asia, so Singapore was the deciding factor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL, DDoS protection, and backups are automatic.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Pro plan is also interesting if you hoard side projects like I do: from $13/mo you get a dedicated server with &lt;em&gt;unlimited&lt;/em&gt; instances, so 10 projects cost the same as 1.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Sign up and create a project
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://portal.pocketbasecloud.com" rel="noopener noreferrer"&gt;portal.pocketbasecloud.com&lt;/a&gt;, sign up, complete the $0 Stripe step.&lt;/p&gt;

&lt;p&gt;Create a &lt;strong&gt;Project&lt;/strong&gt;, then open its &lt;strong&gt;PocketBase&lt;/strong&gt; tab.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Create the instance
&lt;/h2&gt;

&lt;p&gt;Click &lt;strong&gt;New PocketBase&lt;/strong&gt;. The form has three sections:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;General Information&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Instance Name&lt;/em&gt; — this becomes your subdomain, so keep it short. I used &lt;code&gt;my-pocketbase&lt;/code&gt; → &lt;code&gt;https://my-pocketbase.pocketbasecloud.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Description&lt;/em&gt; — optional, skipped it&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;PocketBase Version&lt;/em&gt; — v0.39.3 was the latest when I did this. Take the latest unless you have a reason not to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Admin Credentials&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin email + password (12–20 chars). These are for the PocketBase admin dashboard, the same &lt;code&gt;/_/&lt;/code&gt; login you know from self-hosting.&lt;/li&gt;
&lt;li&gt;There's a copy button next to the password field — copy it into your password manager &lt;em&gt;before&lt;/em&gt; clicking Create.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick the region closest to your users. I picked Singapore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Provisioning
&lt;/h2&gt;

&lt;p&gt;You get a live progress screen while it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;picks a server based on current capacity&lt;/li&gt;
&lt;li&gt;provisions the instance and assigns a port&lt;/li&gt;
&lt;li&gt;sets up DNS&lt;/li&gt;
&lt;li&gt;issues the HTTPS cert&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your backend is now at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;instance-name&amp;gt;.pocketbasecloud.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Log in and sanity-check it
&lt;/h2&gt;

&lt;p&gt;The admin panel is where it always is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;instance-name&amp;gt;.pocketbasecloud.com/_/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in with the credentials from Step 2. It's a completely standard PocketBase admin UI — collections, users, API rules, logs. &lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;todos&lt;/code&gt; collection, added a record, and hit it from the browser console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PocketBase&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pocketbase&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;pb&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;PocketBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://my-pocketbase.pocketbasecloud.com&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;todos&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;pb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getFullList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// real-time works out of the box, no extra config&lt;/span&gt;
&lt;span class="nx"&gt;pb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;e&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;After using PocketBase Cloud for a while, I found that it makes starting a side project much simpler. Instead of spending time setting up servers, SSL, and backups, I can jump straight into building the product.&lt;/p&gt;

&lt;p&gt;If you're already using PocketBase—or you're thinking about trying it for your next project—I recommend giving &lt;strong&gt;&lt;a href="https://pocketbasecloud.com" rel="noopener noreferrer"&gt;PocketBase Cloud&lt;/a&gt;&lt;/strong&gt; a try. The free plan is more than enough to explore the platform and see whether it fits your workflow.&lt;/p&gt;

&lt;p&gt;I hope this guide helps you get started a little faster. If you end up trying it, I'd love to hear what you think. Happy building! 🚀&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>pocketbaseloud</category>
      <category>tutorial</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
