<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[bunny.net Blog]]></title><description><![CDATA[Performance tips, updates, tricks and insights into our Content Delivery Network.]]></description><link>https://bunny.net/blog/</link><image><url>https://bunny.net/blog/favicon.png</url><title>bunny.net Blog</title><link>https://bunny.net/blog/</link></image><generator>Ghost 5.82</generator><lastBuildDate>Wed, 22 Apr 2026 10:43:53 GMT</lastBuildDate><atom:link href="https://bunny.net/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Introducing Bunny Shield API Guardian: protection that understands your API]]></title><description><![CDATA[Today, we’re introducing API Guardian, a new layer of Bunny Shield that brings schema-aware protection to your APIs by turning your OpenAPI definition into enforceable validation at the edge.]]></description><link>https://bunny.net/blog/introducing-bunny-shield-api-guardian-protection-that-understands-your-api/</link><guid isPermaLink="false">69e88354160dc403fbfcf124</guid><category><![CDATA[News]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Joe Connolly]]></dc:creator><pubDate>Wed, 22 Apr 2026 09:00:43 GMT</pubDate><content:encoded><![CDATA[<p>APIs sit at the center of almost everything you build today.</p><p>They handle authentication, power your frontend, connect your services, and move data between them. Requests are syntactically valid, responses return as expected, and systems stay online. Yet requests still reach your application that don&#x2019;t belong there.</p><p>The problem is that most security systems are designed to detect what looks malicious. APIs don&#x2019;t always fail that way. Requests can be completely valid from an HTTP perspective and still violate how your application is supposed to behave.</p><p>That gap between what is valid and what is correct is where API abuse happens. We see it constantly at the edge, where traffic looks valid on the surface but doesn&#x2019;t make sense for the API it&#x2019;s targeting.</p><p>Today, we&#x2019;re introducing <strong>API Guardian</strong>, a new layer of Bunny Shield that brings schema-aware protection to your APIs by turning your OpenAPI definition into enforceable validation at the edge.</p><p>Instead of asking whether a request looks suspicious, API Guardian asks whether it makes sense according to your API contract.</p><h3 id="validate-requests-at-the-edge-using-your-schema">Validate requests at the edge using your schema</h3><p>API Guardian starts with something you already have: your OpenAPI schema.</p><p>When you upload a spec, it becomes an enforceable contract at the edge. Each endpoint is translated into validation rules that run directly inside Bunny Shield&#x2019;s request security pipeline, so invalid requests can be filtered before they ever reach your origin.</p><p>Under the hood, the spec is normalized, schemas are extracted, and references are resolved (internal only) before being compiled into native WAF rules.</p><p>API Guardian enforces safety limits at upload time, including a maximum spec size of 2 MB. These limits cover nesting depth, total schema complexity, and regex usage. These guardrails ensure predictable performance and prevent excessively complex schemas from impacting validation at the edge.</p>
<!--kg-card-begin: html-->
<p>
  Validation covers the full request surface:
  path parameters, query parameters, headers, cookies, and JSON request bodies
  (<code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">application/json</code>
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">*+json</code>).
</p>
<!--kg-card-end: html-->
<p>You can log, block, or ignore requests that don&#x2019;t match, depending on how strictly you want to enforce your API surface.</p><p>Validation events that result in logging or blocking are recorded in your Bunny Shield event logs, so you can see what is being flagged and why.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/04/Bunny-Shield-event-logs.png" class="kg-image" alt loading="lazy" width="1563" height="948" srcset="https://bunny.net/blog/content/images/size/w600/2026/04/Bunny-Shield-event-logs.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/04/Bunny-Shield-event-logs.png 1000w, https://bunny.net/blog/content/images/2026/04/Bunny-Shield-event-logs.png 1563w" sizes="(min-width: 720px) 720px"></figure><h3 id="keep-responses-aligned-with-your-contract">Keep responses aligned with your contract</h3><p>APIs define both the inputs your system accepts and the outputs it produces.</p><p>When requests are malformed or deliberately crafted, responses can behave in unintended ways. Error paths, edge cases, or inconsistent handling can expose fields or data that were never meant to be returned.</p><p>API Guardian can validate responses against your schema on a per-endpoint basis. When enabled, responses are checked before they leave your system, helping prevent unexpected or unsafe output from reaching clients.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/04/API-Guardian-responses-validation.png" class="kg-image" alt loading="lazy" width="1390" height="796" srcset="https://bunny.net/blog/content/images/size/w600/2026/04/API-Guardian-responses-validation.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/04/API-Guardian-responses-validation.png 1000w, https://bunny.net/blog/content/images/2026/04/API-Guardian-responses-validation.png 1390w" sizes="(min-width: 720px) 720px"></figure><h3 id="enforce-authentication-at-the-edge">Enforce authentication at the edge</h3><p>APIs are harder to protect than traditional web traffic because requests often look valid at the protocol level, even when they&#x2019;re not meaningful for the application.</p><p>Most unwanted traffic still fails basic authentication checks. Bot traffic, scanners, and volumetric attacks rarely include correctly formed credentials, and almost never include valid tokens.</p><p>API Guardian enforces authentication requirements defined in your schema before requests reach your origin. It supports API keys (headers, query parameters, or cookies), HTTP authentication (Bearer and Basic), OAuth2, and OpenID Connect.</p><p>Requests missing required credentials are rejected immediately at the edge, reducing unnecessary load and filtering out a large portion of low-effort attack traffic.</p>
<!--kg-card-begin: html-->
<p>
  For Bearer tokens defined with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">bearerFormat: jwt</code>,
  API Guardian performs basic checks such as validating structure and expiration. This allows you to filter out malformed or clearly invalid tokens before forwarding the request, while leaving full authentication and authorization to your application.
</p>
<!--kg-card-end: html-->
<figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/04/Bearer-tokens.png" class="kg-image" alt loading="lazy" width="1393" height="234" srcset="https://bunny.net/blog/content/images/size/w600/2026/04/Bearer-tokens.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/04/Bearer-tokens.png 1000w, https://bunny.net/blog/content/images/2026/04/Bearer-tokens.png 1393w" sizes="(min-width: 720px) 720px"></figure><h3 id="apply-rate-limits-where-they-matter">Apply rate limits where they matter</h3><p>Not every endpoint behaves the same way, so protection shouldn&#x2019;t be uniform.</p><p>Some routes are public and inexpensive. Others are sensitive or resource-intensive. Applying a single rate limit across an entire API rarely reflects how real systems behave.</p><p>API Guardian allows you to define rate limits per endpoint, aligned directly with your API structure.</p>
<!--kg-card-begin: html-->
<p>
  Path templates such as
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">/users/{id}</code>
  work out of the box, so you don&#x2019;t need to define rules for every variation. Rate limits are applied at the template level, meaning all variations of a route share the same counter by default.
</p>

<p>
  You can define limits globally per endpoint or per IP address, with time windows ranging from one second to one hour. Versioned endpoints such as
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">/v1</code>
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">/v2</code>
  are treated independently, allowing you to tune protection as your API evolves.
</p>
<!--kg-card-end: html-->
<figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/04/Bunny-rate-limiting.png" class="kg-image" alt loading="lazy" width="1390" height="223" srcset="https://bunny.net/blog/content/images/size/w600/2026/04/Bunny-rate-limiting.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/04/Bunny-rate-limiting.png 1000w, https://bunny.net/blog/content/images/2026/04/Bunny-rate-limiting.png 1390w" sizes="(min-width: 720px) 720px"></figure><h3 id="combine-structure-with-targeted-protection">Combine structure with targeted protection</h3><p>Schema validation ensures requests are structurally correct, based on your API schema. However, some behaviors still fall outside of structure alone.</p><p>API Guardian allows you to apply targeted injection detection to specific parameters. You can choose which query, path, header, or cookie values should be inspected for patterns like XSS or SQL injection.</p><p>This combines schema validation with traditional WAF protection, giving you precise control over where deeper inspection is applied.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/04/Bunny-injection-detection.png" class="kg-image" alt loading="lazy" width="1392" height="234" srcset="https://bunny.net/blog/content/images/size/w600/2026/04/Bunny-injection-detection.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/04/Bunny-injection-detection.png 1000w, https://bunny.net/blog/content/images/2026/04/Bunny-injection-detection.png 1392w" sizes="(min-width: 720px) 720px"></figure><h3 id="built-into-the-edge-not-added-on-top">Built into the edge, not added on top</h3><p>API Guardian runs directly within the <a href="http://bunny.net/">bunny.net</a> edge as part of the existing request pipeline.</p><p>Validation happens in the same path as the rest of Bunny Shield, so requests don&#x2019;t take an extra hop. This keeps latency low while filtering invalid traffic before it reaches your origin, acting as a first layer of protection for APIs that would otherwise need to handle this traffic themselves.</p><h3 id="availability-and-limits">Availability and limits</h3><p>API Guardian is currently in <a href="https://docs.bunny.net/product-release-stages">public preview</a> on Bunny Shield <strong>Advanced plans and above</strong>.</p><p>We designed a simple, tiered structure to match the needs of different teams: Advanced and Business plans include clear endpoint limits, while Enterprise offers full customization. This keeps things straightforward and focused.</p><p>Endpoint limits scale by plan:</p><ul><li><strong>Advanced</strong>: up to 10 endpoints</li><li><strong>Business</strong>: up to 50 endpoints</li><li><strong>Enterprise</strong>: customizable based on your needs</li></ul><p>We set these limits intentionally. Rather than encouraging blanket protection across every single endpoint (which often leads to noise and maintenance overhead), we want to help teams focus on securing their highest-traffic, most sensitive, and business-critical endpoints.</p><p>API Guardian currently supports <strong>OpenAPI 3.0.x</strong> specifications.</p><p>You can re-upload and evolve your schema at any time. Existing endpoints retain their configuration, while removed endpoints are automatically cleaned up.</p><h3 id="what%E2%80%99s-next-for-bunny-shield">What&#x2019;s next for Bunny Shield</h3><p>API Guardian completes the initial vision for Bunny Shield, bringing structure and intent into how traffic is evaluated at the edge.</p><p>From here, the focus shifts to refining and expanding what&#x2019;s already there. That includes improving existing protections, advancing bot detection to better handle modern traffic patterns, and continuing to evolve the platform based on real-world usage and feedback.</p><p>Because <a href="http://bunny.net/">bunny.net</a> sits at the edge, our global network acts as your first and strongest line of defense. We designed Bunny Shield to secure all your workloads, from website traffic and APIs to AI models, stopping attacks before they ever hit your servers. We have an exciting roadmap ahead.</p><h3 id="start-protecting-your-api-contract">Start protecting your API contract</h3><p>Getting started is straightforward.</p><p>Upload your OpenAPI schema, refine validation, and apply authentication and rate limits per endpoint.</p><p>Start in log mode, observe what your API actually receives, then switch to blocking once you&apos;re confident.</p><p>Security shouldn&#x2019;t require complex rules or constant tuning. With API Guardian, your API contract becomes the source of truth.</p><p><a href="https://dash.bunny.net/auth/login">Log in</a> or <a href="https://dash.bunny.net/auth/register">sign up</a> to upload your schema and start protecting your API.</p>]]></content:encoded></item><item><title><![CDATA[JA4 fingerprinting: a better way to identify clients]]></title><description><![CDATA[JA3 was one of the first widely adopted approaches here. It takes values from the TLS handshake and hashes them into a fingerprint, giving you a way to group similar clients together.]]></description><link>https://bunny.net/blog/ja4-fingerprinting-a-better-way-to-identify-clients/</link><guid isPermaLink="false">69c3b9ea160dc403fbfcf017</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Joe Connolly]]></dc:creator><pubDate>Wed, 25 Mar 2026 11:00:05 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/bunny-JA4-fingerprinting.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/bunny-JA4-fingerprinting.png" alt="JA4 fingerprinting: a better way to identify clients"><p>There was a time when identifying traffic on the internet was relatively straightforward.</p><p>An IP address and a User-Agent were usually enough to make a decision. If something looked wrong, you blocked it. Otherwise, you let it through.</p><p>That approach no longer holds up.</p><p>Today, a single automated client can appear as thousands of different users, rotating IPs, mutating headers, and mimicking real browsers with surprising accuracy. Botnets operate at scale across large networks, while headless frameworks and AI-driven agents make it even easier to blend in.</p><p>At that point, what a client claims to be is no longer something you can rely on. You need a signal that is much harder to fake.</p><h3 id="looking-at-what-clients-can%E2%80%99t-easily-fake">Looking at what clients can&#x2019;t easily fake</h3>
<!--kg-card-begin: html-->
<p>
  Every HTTPS connection starts with a TLS handshake. Before any request is sent, the client introduces itself through a
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">ClientHello</code>
  message that reflects its underlying TLS stack, including supported versions, cipher suites, extensions, and protocol preferences.
</p>
<!--kg-card-end: html-->
<p>Unlike headers, which are trivially manipulated, the TLS handshake reflects the client&#x2019;s underlying implementation and is far harder to replicate.</p><p>JA3 was one of the first widely adopted approaches here. It takes values from the TLS handshake and hashes them into a fingerprint, giving you a way to group similar clients together.</p><p>For a while, it worked well. However, it relies heavily on the exact ordering of fields in the handshake, which introduces a key limitation.</p><p>Modern browsers and privacy-focused clients often shuffle TLS extensions specifically to avoid being tracked. From a functionality point of view, nothing has changed, but to JA3 it looks like a completely different client.</p><p>The same browser can produce multiple fingerprints, making it harder to reason about. At the same time, attackers can exploit this by slightly tweaking the ordering or configuration to avoid matching known fingerprints.</p><p>JA4 addresses this directly by removing those inconsistencies.</p><p>Instead of hashing the raw handshake as-is, it normalizes the data first. It focuses on the parts that actually describe the client and removes noise like ordering differences.</p><p>The result is a fingerprint that stays consistent for the same client, even with those small variations, and is much harder to manipulate without actually changing the underlying TLS stack.</p><h3 id="a-more-stable-way-to-fingerprint-clients">A more stable way to fingerprint clients</h3><p>A JA4 fingerprint is a compact string like this:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

t13d1516h2_8daaf6152771_02713d6af862

</div>
<!--kg-card-end: html-->
<p>At first glance it looks cryptic, but it is not random.</p><p>It is made up of multiple parts, each describing a different aspect of the TLS handshake.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/bunny.net-JA4-explanation.png" class="kg-image" alt="JA4 fingerprinting: a better way to identify clients" loading="lazy" width="2000" height="969" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/bunny.net-JA4-explanation.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/bunny.net-JA4-explanation.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/bunny.net-JA4-explanation.png 1600w, https://bunny.net/blog/content/images/size/w2400/2026/03/bunny.net-JA4-explanation.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Once you break it down, it becomes much easier to reason about. You are not just looking at a hash; you are looking at a structured summary of how that client speaks TLS.</p><p>That structure is what makes it practical.</p><p>Instead of treating the fingerprint as a single opaque value, you can match on parts of it, group similar clients together, or spot outliers that do not fit expected patterns.</p><p>And because those parts are derived from the client&#x2019;s actual implementation, they tend to stay stable even when everything around them changes.</p><p>JA4 is not meant to uniquely identify individual users. It is a signal that helps group similar clients and works best when combined with other data like IP addresses, request headers, and behavior.</p><h3 id="ja4-built-into-bunnynet">JA4, built into bunny.net</h3><p>We&apos;ve incorporated JA4 into our Layer 7 DDoS mitigation and bot detection systems to group related activity and make more accurate decisions without relying on easily spoofed signals.</p>
<!--kg-card-begin: html-->
<p>
  And since JA4 fingerprinting is now computed automatically for every HTTPS request passing through bunny.net, each request is assigned a fingerprint at the edge and forwarded to your origin via the <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">CDN-JA4</code> header.
</p>
<!--kg-card-end: html-->
<p>That gives you direct access to the same signal we use internally, with full details available in the <a href="https://docs.bunny.net/cdn/security/ja4-fingerprinting">JA4 fingerprinting documentation</a>.</p><p>Because the fingerprint reflects the client&#x2019;s actual TLS implementation, it stays consistent even when other identifiers change. That makes it particularly useful for tracking automated traffic that rotates IPs or mutates headers between requests.</p><p>For example, traffic coming from hundreds or thousands of different IP addresses may still share the same JA4 fingerprint, letting you group and act on it as a single source.</p><h3 id="putting-ja4-to-work-with-bunny-shield">Putting JA4 to work with Bunny Shield</h3><p>JA4 is most useful when you can act on it directly.</p><p>We have integrated JA4 deeply into Bunny Shield so you can use it to improve your security posture at the edge.</p><p>You can create rate limits based on JA4, or combine it with IP addresses for tighter control. You can write custom WAF rules that match a full fingerprint or even specific parts of it. You can also build access lists around known JA4 identities to block, allow, or challenge specific clients.</p><p>This is especially useful when dealing with distributed traffic. Even if requests are spread across many IPs, they often still share the same underlying fingerprint. Instead of chasing individual requests, you can act on the source implementation itself.</p><p>The end result is simple. You are no longer limited to what a client claims to be. You can make decisions based on how it is actually built.</p><h3 id="a-better-signal-for-a-noisier-internet">A better signal for a noisier internet</h3><p>Relying on IPs and headers is becoming less effective as traffic becomes more distributed and easier to disguise.</p><p>JA4 gives you a more stable way to understand what is behind each request. Instead of chasing constantly changing surface-level signals, you can start grouping and acting on traffic based on how it is implemented.</p><p>It&#x2019;s already available on every request passing through bunny.net and fully integrated into Bunny Shield, so you can put it to use immediately.</p><p>If you want a clearer view of your traffic or tighter control over how it behaves, this is a good place to start.</p><p><a href="https://dash.bunny.net/auth/login">Log in</a> to explore JA4, or <a href="https://dash.bunny.net/auth/register">sign up</a> to get started in minutes.</p>]]></content:encoded></item><item><title><![CDATA[Introducing pattern matching for Edge Rules]]></title><description><![CDATA[If you're already familiar with Lua patterns, everything behaves exactly as you would expect. If not, the examples below should give you a good feel for how they can be used in practice.]]></description><link>https://bunny.net/blog/introducing-pattern-matching-for-edge-rules/</link><guid isPermaLink="false">69ba99ff160dc403fbfcefd7</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Joe Connolly]]></dc:creator><pubDate>Thu, 19 Mar 2026 09:00:00 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Bunny-Pattern-Matching.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Bunny-Pattern-Matching.png" alt="Introducing pattern matching for Edge Rules"><p>Edge Rules allow you to define logic that runs directly on bunny.net&#x2019;s edge servers before a request reaches your origin. They can modify caching behavior, redirect traffic, route requests to different origins, apply security actions, and much more based on conditions like the request URL, headers, query string, or hostname.</p><p>A common part of writing Edge Rules is matching requests based on these values. In many cases, a simple string or wildcard match is enough, but modern applications often generate URLs that follow predictable patterns rather than exact values. Streaming manifests, versioned build assets, and dynamically generated API routes are all good examples.</p><p>To make these scenarios easier to handle, we&#x2019;ve added pattern matching support to Edge Rule conditions.</p><h2 id="using-pattern-matching-in-edge-rules">Using pattern matching in Edge Rules</h2><p>Pattern matching can be used in any Edge Rule condition that evaluates a value, such as the request URL, headers, or query string.</p><p>To indicate that a condition should use pattern matching, write the value using the following format:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#C6979E;">pattern</code><code style="color:#E9BC4F;">:^...</code><code style="color:#ffffff;">$</code>

</div>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>
  The
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">pattern:</code>
  prefix tells the Edge Rule engine to evaluate the value using Lua&#x2019;s pattern matching instead of a normal string comparison. Lua patterns are a lightweight pattern-matching system similar to regular expressions, but intentionally simpler and faster.
</p>

<p>
  The pattern itself should be wrapped with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">^</code>
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">$</code>
  so the entire value is matched. If the prefix is not present, the condition behaves as a normal string comparison.
</p>

<p>For example:</p>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#C6979E;">pattern</code><code style="color:#E9BC4F;">:^.*</code>/video_chunk<code style="color:#E9BC4F;">%-</code>[<code style="color:#E9BC4F;">^</code><code style="color:#E9BC4F;">%-</code>]<code style="color:#E9BC4F;">+</code><code style="color:#E9BC4F;">%-</code>[<code style="color:#E9BC4F;">^</code><code style="color:#E9BC4F;">%-</code>]<code style="color:#E9BC4F;">+</code><code style="color:#E9BC4F;">%</code>.dash$

</div>
<!--kg-card-end: html-->
<p>This pattern matches URLs such as:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#E9BC4F;">/</code>live<code style="color:#E9BC4F;">/</code>video_chunk<code style="color:#E9BC4F;">-</code>us<code style="color:#E9BC4F;">-</code><code style="color:#C6979E;">12345</code>.dash
<br>
<code style="color:#E9BC4F;">/</code>live<code style="color:#E9BC4F;">/</code>video_chunk<code style="color:#E9BC4F;">-</code>es<code style="color:#E9BC4F;">-</code><code style="color:#C6979E;">54321</code>.dash

</div>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>
  Internally, the match is evaluated using Lua&#x2019;s
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">string.find</code>
  function, which performs pattern matching without requiring a full regular expression engine while still supporting useful pattern features such as character classes, repetition operators, and escaped characters.
</p>
<!--kg-card-end: html-->
<p>If you&apos;re already familiar with Lua patterns, everything behaves exactly as you would expect. If not, the examples below should give you a good feel for how they can be used in practice.</p><h2 id="pattern-matching-in-action">Pattern matching in action</h2><p>With pattern matching available, many common CDN scenarios become easier to express with a single condition.</p><p>Below are a few examples where this simplifies Edge Rules while adding a great deal of flexibility.</p><h3 id="matching-streaming-manifests">Matching streaming manifests</h3><p>Streaming platforms often generate manifest files that include region identifiers, channel names, or session information in the filename, such as:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

/live/video_chunk-us-12345.dash
<br>
/live/video_chunk-es-54321.dash

</div>
<!--kg-card-end: html-->
<p>A single condition can match these requests and, for example, apply custom cache control:</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Custom-Cache-Control.png" class="kg-image" alt="Introducing pattern matching for Edge Rules" loading="lazy" width="1658" height="1297" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Custom-Cache-Control.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/Custom-Cache-Control.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/Custom-Cache-Control.png 1600w, https://bunny.net/blog/content/images/2026/03/Custom-Cache-Control.png 1658w" sizes="(min-width: 720px) 720px"></figure><h3 id="targeting-versioned-assets">Targeting versioned assets</h3><p>Many build systems generate static assets with version numbers in the filename.</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

/assets/app-1.4.7.js
<br>
/assets/app-1.4.8.js
<br>
/assets/app-1.5.0.js

</div>
<!--kg-card-end: html-->
<p>A single condition can match them and apply actions such as rate limiting:</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Rate-Limiting.png" class="kg-image" alt="Introducing pattern matching for Edge Rules" loading="lazy" width="1655" height="1300" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Rate-Limiting.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/Rate-Limiting.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/Rate-Limiting.png 1600w, https://bunny.net/blog/content/images/2026/03/Rate-Limiting.png 1655w" sizes="(min-width: 720px) 720px"></figure><h3 id="blocking-suspicious-requests">Blocking suspicious requests</h3><p>Pattern matching is also useful for security rules.</p><p>For example, you might want to block requests targeting administrative PHP endpoints when the expected session cookie is missing:</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/expected-session-cookie.png" class="kg-image" alt="Introducing pattern matching for Edge Rules" loading="lazy" width="1652" height="1666" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/expected-session-cookie.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/expected-session-cookie.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/expected-session-cookie.png 1600w, https://bunny.net/blog/content/images/2026/03/expected-session-cookie.png 1652w" sizes="(min-width: 720px) 720px"></figure>
<!--kg-card-begin: html-->
<h3>Pattern matching cheat sheet</h3>

<p>
  <strong>Format:</strong> Condition values must start with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">pattern:^</code>
  and end with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">$</code>
  (e.g.,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">pattern:^...$</code>).
  Edge Rules evaluate the expression with Lua&#x2019;s
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">string.find</code>.
</p>

<p>
  <strong>Anchors:</strong> Always use
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">^</code>
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">$</code>
  to match the entire value.
</p>

<p><strong>Common classes:</strong></p>
<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%d</code> digit</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%a</code> letter</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%w</code> alnum + underscore</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.</code> any character</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">[abc]</code> match any listed character</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">[^abc]</code> match any character not listed</li>
</ul>

<p><strong>Repeaters:</strong></p>
<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">+</code> = 1 or more</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">*</code> = 0 or more (greedy)</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">-</code> = 0 or more (non-greedy)</li>
</ul>

<p>
  <strong>Escape:</strong> Use
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%</code>
  to escape special chars, for example
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%.</code>
  for a literal dot and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">%-</code>
  for a hyphen.
</p>

<p>
  <strong>Limitations:</strong> No
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">|</code>
  alternation, no lookaheads/lookbehinds, and no full PCRE. Patterns are intentionally small and fast.
</p>
<!--kg-card-end: html-->
<p>Full reference and examples: <a href="https://docs.bunny.net/cdn/edge-rules/pattern-matching" rel="noopener noreferrer">https://docs.bunny.net/cdn/edge-rules/pattern-matching</a></p><h2 id="why-lua-pattern-matching">Why Lua pattern matching?</h2><p>Lua patterns provide a good balance between expressive matching and predictable performance.</p><p>Unlike full regular expressions, Lua patterns are intentionally simpler and implemented directly in Lua&#x2019;s standard library. This avoids the overhead of a full regex engine while still supporting the most commonly needed matching features such as character classes, repetition operators, and captures.</p><p>For edge environments where rules are evaluated on every request, this simplicity is important. Pattern evaluation remains fast, deterministic, and lightweight, even under very high request volumes across the network.</p><p>Using Lua&#x2019;s native pattern matching also keeps the implementation compact and reliable across all edge nodes while still giving developers enough flexibility to match structured URLs, headers, and request values in practical scenarios.</p><h2 id="try-pattern-matching-in-edge-rules">Try pattern matching in Edge Rules</h2><p>Pattern matching is now available in Edge Rule conditions and can be used anywhere a request value is evaluated, including URLs, headers, cookies, and query strings.</p><p>This makes it possible to express much more precise request logic without creating multiple rules or pushing filtering back to the origin. In many cases, complex matching can now be handled with a single condition running directly at the edge.</p><p>If you&apos;re already using Edge Rules, try replacing some of your existing conditions with pattern matching and see how much simpler your rules can become.</p><p>If you haven&apos;t explored Edge Rules yet, this is a great place to start.</p><p><a href="https://dash.bunny.net/auth/login">Log in</a> or <a href="https://dash.bunny.net/auth/register">sign up</a> for a bunny.net account to create your first pattern matching Edge Rule.</p>]]></content:encoded></item><item><title><![CDATA[Introducing the interactive Bunny Database shell]]></title><description><![CDATA[The interactive Bunny Database shell is currently in public preview. While fully functional, we recommend exercising caution with production workloads as we continue refining the experience.]]></description><link>https://bunny.net/blog/introducing-the-interactive-bunny-database-shell/</link><guid isPermaLink="false">69b92c4b160dc403fbfcef56</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jamie Barton]]></dc:creator><pubDate>Tue, 17 Mar 2026 14:45:00 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Bunny-Database-Shell.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Bunny-Database-Shell.png" alt="Introducing the interactive Bunny Database shell"><p>When we started building <a href="https://docs.bunny.net/database">Bunny Database</a>, we needed a shell-like experience. Naturally, we reached for <a href="https://github.com/libsql/libsql-shell-go">libsql-shell-go</a>, the official Go-based shell from the libSQL project. It&apos;s a solid tool, and it served us well in early development.</p><p>But as we began building the upcoming bunny.net CLI in TypeScript, maintaining a separate Go binary for just the shell felt like carrying two toolchains for one job. We wanted a shell that could be embedded directly into our CLI, share the same authentication flow, and ship as a single binary. So we rewrote it from scratch in TypeScript.</p>
<!--kg-card-begin: html-->
<p>
  The result is
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">@&#x200B;bunny&#x200B;.&#x200B;net/d&#x200B;atabase-shell</code>
  &#x2014; a standalone, interactive SQL shell for libSQL databases that also powers
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">bunny db shell</code>
  inside the upcoming bunny.net CLI.
</p>
<!--kg-card-end: html-->
<p>The interactive Bunny Database shell is currently in public preview. While fully functional, we recommend exercising caution with production workloads as we continue refining the experience.</p><h2 id="connect-and-query">Connect and query</h2><p>You will need Node.js 18 or later installed and an existing database to continue. If you don&apos;t have one, <a href="http://docs.bunny.net/database/quickstart">create one</a>.</p><p>Navigate to <strong>Dashboard &gt; Edge Platform &gt; Database &gt; [Select Database] &gt; Access</strong> to find your database URL and generate an access token.</p>
<!--kg-card-begin: html-->
<p>
  Once you&#x2019;re ready, execute the
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">npx</code>
  command to connect to your database:
</p>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">
npx @&#x200B;bunny&#x200B;.net&#x200B;/database&#x200B;-shell libsql://... --token ...
</div>
<!--kg-card-end: html-->
<p>You get a readline-powered REPL with multi-line SQL support, persistent command history, and query timing:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

&#x2713; Connected to database
<br>
&#xA0;&#xA0;Type .help <code style="color:#779FC9;">for</code> commands, .quit to exit.
<br>
<br>
&#x2192;&#xA0;&#xA0;SELECT <code style="color:#E1E395;">id</code>, name, email FROM <code style="color:#E1E395;">users</code>
<br>
&#xA0;&#xA0;&#xA0;WHERE created_at <code style="color:#E3B74D;">&gt;</code> <code style="color:#BDD95F;">&apos;2025-01-01&apos;</code>;
<br>
&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x252C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x252C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2510;
<br>
&#x2502; <code style="color:#E1E395;">id</code> &#x2502; name&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; email&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502;
<br>
&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x253C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x253C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;
<br>
&#x2502; 1&#xA0;&#xA0;&#x2502; Alice&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; a****e@example.com&#xA0;&#xA0;&#x2502;
<br>
&#x2502; 2&#xA0;&#xA0;&#x2502; Bob&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; b****b@example.com&#xA0;&#xA0;&#x2502;
<br>
&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2534;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2534;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;
<br>
2 rows (4ms)

</div>
<!--kg-card-end: html-->
<h2 id="dot-commands-for-database-introspection">Dot-commands for database introspection</h2>
<!--kg-card-begin: html-->
<p>
  If you&apos;ve used SQLite or libSQL&#x2019;s shell, these will feel familiar. Dot-commands give you quick access to your schema without writing
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">SELECT</code>
  queries against
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">sqlite_master</code>:
</p>

<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.tables</code> list all tables</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.schema [table]</code> shows CREATE statements</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.describe table</code> column names, types, nullability, defaults, and primary keys</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.indexes [table]</code> list indexes with their target columns</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.fk table</code> show foreign key relationships</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.er</code> display a text-based entity-relationship overview of your entire schema, which is useful for getting to grips in an unfamiliar database without jumping through <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.describe</code> calls</li>
</ul>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

&#x2192;&#xA0;&#xA0;.er
<br>
<br>
<code style="color:#E1E395;">users</code>
<br>
&#xA0;&#xA0;<code style="color:#E1E395;">id</code>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;INTEGER&#xA0;&#xA0;PK
<br>
&#xA0;&#xA0;email&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;TEXT
<br>
&#xA0;&#xA0;created_at&#xA0;TEXT
<br>
&#xA0;&#xA0;&#x2514;&#x2500; orders.user_id
<br>
<br>
orders
<br>
&#xA0;&#xA0;<code style="color:#E1E395;">id</code>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;INTEGER&#xA0;&#xA0;PK
<br>
&#xA0;&#xA0;user_id&#xA0;&#xA0;&#xA0;&#xA0;INTEGER&#xA0;&#xA0;FK &#x2192; <code style="color:#E1E395;">users</code>.<code style="color:#E1E395;">id</code>
<br>
&#xA0;&#xA0;total&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;REAL
<br>
&#xA0;&#xA0;created_at&#xA0;TEXT

</div>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>For data operations:</p>

<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.count table</code> row count</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.size table</code> storage size estimate</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.dump [table]</code> export as SQL INSERT statements</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.read file.sql</code> execute a SQL file</li>
</ul>

<p>
  Bunny Database charges against a read quota based on rows scanned. Commands that scan full tables
  (<code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.count</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.size</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.dump</code>)
  warn you before running and ask for confirmation. So an accidental query on a large table doesn&#x2019;t burn through your quota unexpectedly.
</p>
<!--kg-card-end: html-->
<h2 id="saved-views">Saved views</h2><p>Dot-commands are great for introspection, but some queries you run over and over. Saved views let you name and persist any query so you can re-run it without retyping.</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

&#x2192;&#xA0;&#xA0;SELECT u.name, count(o.<code style="color:#E1E395;">id</code>) as orders, sum(o.total) as revenue
<br>
&#xA0;&#xA0;&#xA0;FROM users u JOIN orders o ON o.user_id = u.<code style="color:#E1E395;">id</code>
<br>
&#xA0;&#xA0;&#xA0;GROUP BY u.<code style="color:#E1E395;">id</code> ORDER BY revenue DESC LIMIT 10;
<br>
&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x252C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x252C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2510;
<br>
&#x2502; name&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; orders &#x2502; revenue &#x2502;
<br>
&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x253C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x253C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;
<br>
&#x2502; Alice&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; <code style="color:#C6979E;">42</code>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; 8400.00 &#x2502;
<br>
&#x2502; Bob&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; <code style="color:#C6979E;">31</code>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x2502; 6200.00 &#x2502;
<br>
&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2534;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2534;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;
<br>
<code style="color:#C6979E;">2</code> rows (12ms)
<br>
<br>
&#x2192;&#xA0;&#xA0;.save <code style="color:#BDD95F;">top-customers</code>
<br>
&#x2713; Saved view <code style="color:#BDD95F;">&apos;top-customers&apos;</code>

</div>
<!--kg-card-end: html-->
<p>Next session, next machine, same query:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">
&#x2192;  .view top-customers
</div>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>The following commands cover the full lifecycle:</p>

<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.save NAME</code> saves the last executed query as a named view</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.view NAME</code> executes a saved view</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.views</code> lists all saved views for this database</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.unsave NAME</code> deletes a saved view</li>
</ul>

<p style="font-style: italic;">
  Names follow the same rules as filenames: alphanumeric, hyphens, and underscores only. No dots, slashes, or spaces.
</p>

<h3>Personal and shared views</h3>

<p>
  Views are stored as plain
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.sql</code>
  files, scoped per database. They live in your global config directory
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">~/.config/bunny/views/&lt;database-id&gt;/</code>,
  personal to you and available across every project.
</p>

<p>
  You can override the storage location with the
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">--views-dir</code>
  flag to point to any directory, for example, a folder inside your repo. Because views are just
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.sql</code>
  files, you can commit them and share them with your team: common reporting queries, debugging helpers, and onboarding examples. Everyone gets the same set.
</p>

<h2>Five output modes</h2>

<p>
  Switch formats on the fly with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.mode</code>:
</p>

<ul>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">default</code> clean, borderless columns</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">table</code> bordered ASCII table</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">json</code> array of objects, ready for piping to <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">jq</code></li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">csv</code> proper RFC-compliant CSV with escaped fields</li>
  <li><code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">markdown</code> GitHub-flavored pipe tables, useful for pasting into issues or docs</li>
</ul>

<p>
  You can also set the mode at launch with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">--mode json</code>
  for scripting.
</p>

<h2>Automatic sensitive column masking</h2>

<p>
  Most of the time, you don&#x2019;t actually need to see the raw value of a
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">password_hash</code>
  or
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">api_key</code>
  column; you just need to know it&#x2019;s there. The shell masks sensitive column names automatically, keeping raw secrets out of your terminal, your scrollback buffer, and anything you pipe or paste.
</p>

<p>
  Detected patterns include
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">password</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">secret</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">api_key</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">auth_token</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">ssn</code>,
  and similar names. Emails get a partial mask
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">a****e@example.com</code>;
  secrets are fully redacted
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">********</code>.
</p>

<p>
  This works across all output modes, not just the interactive terminal. If you&apos;re exporting to CSV or JSON, masked columns stay masked.
</p>

<p>
  Toggle it off with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.unmask</code>
  when you actually need the raw values, or launch with
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">--unmask</code>.
</p>
<!--kg-card-end: html-->
<h2 id="non-interactive-mode">Non-interactive mode</h2><p>For scripting or quick lookups, skip the REPL entirely:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

# <code style="color:#948168;">Install globally</code>
<br>
<code style="color:#E1E395;">npm install</code> -g @&#x200B;bunny.&#x200B;net&#x200B;/database&#x200B;-shell
<br>
<br>
# <code style="color:#948168;">Inline query</code>
<br>
bsql libsql://my-database.bunnydb.net --token ... <code style="color:#BDD95F;">&quot;SELECT count(*) FROM orders&quot;</code>
<br>
<br>
# <code style="color:#948168;">Execute a file</code>
<br>
bsql libsql://my-database.bunnydb.net --token ... seed.sql
<br>
<br>
# <code style="color:#948168;">JSON output for scripting</code>
<br>
bsql libsql://my-database.bunnydb.net --token ... --mode json <code style="color:#BDD95F;">&quot;SELECT * FROM products&quot;</code>

</div>
<!--kg-card-end: html-->
<p>File execution splits on semicolons (properly handling quoted strings and comments) and stops on the first error.</p>
<!--kg-card-begin: html-->
<h2>Thank you <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">libsql-shell-go</code></h2>

<p>
  We want to acknowledge the
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">libsql-shell-go</code>
  project. It set the standard for what a libSQL shell should feel like: the dot-commands, the output modes, and the overall interaction model.
</p>

<p>
  We released an early look at a wrapper on top of
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">libsql-shell-go</code>,
  but it was clear that we could do more to improve the experience for Bunny Database users. Our implementation follows the same spirit while adapting it to the TypeScript ecosystem and the bunny.net developer experience.
</p>

<h2>Why rewrite in TypeScript?</h2>

<p>Three reasons:</p>

<ul>
  <li>
    <strong>Single dependency chain</strong> &#x2014; The upcoming bunny.net CLI is TypeScript. Embedding the shell directly means no sidecar binary, no version drift, and no separate release pipeline.
  </li>
  <li>
    <strong>Shared auth and config</strong> &#x2014; When you run
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">bunny db shell</code>,
    the CLI resolves your database credentials from your project&apos;s
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.env</code>,
    your authenticated profile, or explicit flags. The same way every other upcoming
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">bunny db</code>
    command works. A separate Go binary can&apos;t participate in that flow without shelling out or duplicating logic.
  </li>
  <li>
    <strong>Standalone when you want it</strong> &#x2014; Despite being built for the bunny.net CLI,
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">bsql</code>
    is published as its own package
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">(@&#x200B;bunny&#x200B;.net&#x200B;/database&#x200B;-shell)</code>
    with zero CLI dependencies. You can use it as a library.
  </li>
</ul>
<!--kg-card-end: html-->
<h2 id="get-started">Get started</h2><p>Install the bunny.net CLI and connect to your first database:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#E1E395;">npm install</code> -g @bunny.net/database-shell
<br>
bsql

</div>
<!--kg-card-end: html-->
<p>Or use the database-shell directly via npx:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

npx @b&#x200B;unny&#x200B;.net/&#x200B;database&#x200B;-&#x200B;shell libsql://your-database.bunnydb.net --token your-token

</div>
<!--kg-card-end: html-->
<p>Make sure to <a href="https://discord.com/invite/bunnynet" rel="noopener">join us on Discord</a> to share your experience and any feedback.</p><h2 id="whats-next">What&apos;s next</h2><p>A platform is only as good as its developer experience, and that experience increasingly lives in the terminal. Our goal with the bunny.net CLI is to give you a single tool for everything on the bunny.net stack. No tab-switching, no copy-pasting tokens between dashboards. The interactive Database shell is the first piece of that vision.</p><p>Over the coming weeks, we&#x2019;ll be rolling out commands for Database CRUD, Magic Containers, Edge Scripting, and Storage. The goal is a workflow where you can go from an empty project to a deployed, queryable application without ever leaving your terminal.</p>]]></content:encoded></item><item><title><![CDATA[Sovereign cloud and edge: bunny.net and UpCloud partner to power your global growth]]></title><description><![CDATA[For those new to our Finnish partner, they are a performance-obsessed European cloud services provider renowned for their commitment to transparency, cloud sovereignty, and delivering amazing developer experiences to over 10 thousand developers and enterprises alike.]]></description><link>https://bunny.net/blog/sovereign-cloud-and-edge-bunny-net-and-upcloud-partner-to-power-your-global-growth/</link><guid isPermaLink="false">69b93126160dc403fbfcef7b</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Graeme Inglis]]></dc:creator><pubDate>Tue, 17 Mar 2026 10:48:46 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Bunny-Upcloud.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Bunny-Upcloud.png" alt="Sovereign cloud and edge: bunny.net and UpCloud partner to power your global growth"><p>We have some &quot;ear-resistible&quot; news to share! bunny.net and <a href="https://upcloud.com/blog/upcloud-bunny-partner-power-sovereign-cloud-edge/" rel="noreferrer"><strong>UpCloud</strong> are officially teaming up</a> to offer a unified, high-performance, and digitally sovereign cloud-to-edge ecosystem.</p><p>In today&#x2019;s fast-paced world, you shouldn&apos;t have to choose between innovation and privacy. This strategic partnership enables organizations across both public and private sectors to access a European sovereign cloud and edge infrastructure through a seamless, integrated experience.</p><p>Together with <strong>UpCloud,</strong> we&apos;re providing you with a powerful solution to deploy, scale, protect, and deliver applications with total confidence.</p><h3 id="who-is-upcloud">Who is UpCloud?</h3><p>Sometimes partnerships just make sense. That is exactly what we saw in the team at UpCloud, a reflection of what we hold sacred at bunny.net.</p><p>For those new to our Finnish partner, they are a performance-obsessed European cloud services provider renowned for their commitment to transparency, cloud sovereignty, and delivering amazing developer experiences to over 10 thousand developers and enterprises alike.</p><h3 id="what-this-partnership-unlocks">What this partnership unlocks</h3><p>This collaboration is about more than just speed and security. It&apos;s about providing a European <strong>single-point-of-purchase</strong> environment that maintains data sovereignty at its core. Whether you&apos;re an independent developer or part of a large enterprise, this partnership focuses on how you can grow without boundaries.</p><ul><li><strong>Digital sovereignty as standard:</strong> Pair a high-performing European cloud platform with a global edge network, built and operated in the EU and available everywhere.</li><li><strong>Unified performance:</strong> Accelerate and secure your UpCloud-hosted websites and applications for a worldwide audience instantly, using UpCloud&#x2019;s enterprise-grade infrastructure with bunny.net&#x2019;s <strong>250</strong> Tbps+ network capacity.</li><li><strong>Seamless integration:</strong> Manage your stack efficiently with Bunny CDN and Shield integrated directly within the UpCloud Hub and API.</li></ul><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Dejan-Quote-blog.png" class="kg-image" alt="Sovereign cloud and edge: bunny.net and UpCloud partner to power your global growth" loading="lazy" width="2000" height="255" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Dejan-Quote-blog.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/Dejan-Quote-blog.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/Dejan-Quote-blog.png 1600w, https://bunny.net/blog/content/images/size/w2400/2026/03/Dejan-Quote-blog.png 2400w" sizes="(min-width: 720px) 720px"></figure><h3 id="coming-to-a-burrow-near-you-in-summer-2026"><strong>Coming to a burrow near you in summer 2026</strong></h3><p>While our teams are already busy working to bring these services to your fingertips, please note that the integrated solution is currently in development and will be available starting in summer 2026. We&apos;re extremely excited to offer a massive hop forward for businesses looking to stay secure without compromising end-user data sovereignty.</p><h3 id="join-the-mission-for-a-better-internet"><strong>Join the mission for a better internet</strong></h3><p>bunny.net is a European company on a mission to make the internet hop faster! Headquartered in Slovenia, we power millions of websites worldwide. By joining forces with Finland-based <strong>UpCloud</strong>, we&#x2019;re doubling down on our commitment to building better, more secure internet experiences for everyone.</p><p>Don&#x2019;t let compliance hurdles or privacy concerns limit your global reach. If you&#x2019;d like to discover more about how our joint solution can help your business hop ahead of the competition, reach out to <a href="mailto:hello@upcloud.com">hello@upcloud.com</a> to learn more!</p>]]></content:encoded></item><item><title><![CDATA[Introducing the new Bunny Stream video player]]></title><description><![CDATA[From this point onward, new capabilities and features added to Bunny Stream will only be supported by the new player. At the same time, we don’t currently have plans to sunset the legacy player, so you won’t be forced to migrate. Your current embed URLs will continue to work.]]></description><link>https://bunny.net/blog/introducing-the-new-bunny-stream-video-player/</link><guid isPermaLink="false">69b2abf4160dc403fbfceeef</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Thu, 12 Mar 2026 13:03:22 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Bunny-Stream-2.0.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Bunny-Stream-2.0.png" alt="Introducing the new Bunny Stream video player"><p>When you&#x2019;re building video capabilities, the player component is the part of your video stack that&#x2019;s experienced directly by your users.</p><p>Today, we&#x2019;re glad to help improve their experience with the new Bunny Stream video player becoming generally available.</p><p>The new player offers:</p><ul><li><strong>Improved user interface</strong></li><li><strong>Faster, smoother playback</strong></li><li><strong>Consistency across all major browsers</strong></li><li><strong>Accessibility improvements</strong></li></ul><p>In this post, we&#x2019;ll cover:</p><ul><li>What this release means for new and existing video libraries</li><li>How to upgrade to the new player</li><li>What&#x2019;s under the hood and where the ecosystem is heading</li></ul><p>Let&#x2019;s get into it.</p><h2 id="how-the-new-player-rolls-out">How the new player rolls out</h2><ul><li><strong>New libraries use the new player by default</strong>. New video libraries created in Bunny Stream will automatically use the new player. If needed, you can switch a library back to the legacy player after it&#x2019;s created via a toggle in the dashboard or using the <a href="https://docs.bunny.net/api-reference/core/stream-video-library/update-video-library" rel="noreferrer">&apos;update video library&apos;</a> API endpoint.</li><li><strong>Existing libraries stay on the legacy player until you migrate them</strong>. Your existing libraries and embed URLs will continue to use the legacy player until you choose to migrate them to the new one.</li></ul><p>From this point onward, new capabilities and features added to Bunny Stream will only be supported by the new player. At the same time, we don&#x2019;t currently have plans to sunset the legacy player, so you won&#x2019;t be forced to migrate. Your current embed URLs will continue to work.</p><p>That said, migration is straightforward and your users will surely feel the difference, so we recommend you take one of the paths described below.</p><h2 id="how-to-upgrade-to-the-new-player">How to upgrade to the new player</h2>
<!--kg-card-begin: html-->
<h3>If you&#x2019;re not using custom CSS or JavaScript</h3>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<ul>
  <li>Log in to bunny.net</li>
  <li>Navigate to <strong>Stream</strong> from the sidebar</li>
  <li>Select the <strong>Video Library</strong> you&#x2019;d like to use the new player on</li>
  <li>Open <strong>Player Settings</strong></li>
  <li>Toggle off <strong>Enable Legacy Player</strong></li>
  <li>
    <strong>Update your embed URLs to use the new player endpoint:</strong> replace<br>
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">iframe&#x200B;.mediadelivery.&#x200B;net/&#x200B;embed/</code>
    with
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">player&#x200B;.mediadelivery.&#x200B;net/&#x200B;embed/</code>
  </li>
</ul>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>
  Alternatively, you can also upgrade your video library to the new player using the API by setting 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">&quot;PlayerVersion&quot;: 2</code>, 
  like in the following example:
</p>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#BDDA60;">curl</code> --request POST \
<br>
&#xA0;&#xA0;--url h&#x200B;ttps:/&#x200B;/&#x200B;api.&#x200B;bunny.net/&#x200B;videolibrary&#x200B;/{&#x200B;libraryId} \
<br>
&#xA0;&#xA0;--header <code style="color:#BDDA60;">&apos;AccessKey: YOUR_ACCESS_KEY&apos;</code> \
<br>
&#xA0;&#xA0;--header <code style="color:#BDDA60;">&apos;Content-Type: application/json&apos;</code> \
<br>
&#xA0;&#xA0;--data &apos;
<br>
{
<br>
&#xA0;&#xA0;<code style="color:#BDDA60;">&quot;PlayerVersion&quot;</code>: <code style="color:#C7989E;">2</code>
<br>
}

</div>
<!--kg-card-end: html-->
<p></p><p><strong>Note</strong>: Updating the video library via the dashboard or API does not automatically update your existing embed URLs. To use the new player endpoint, update your embed URLs to player.mediadelivery.net/embed/ instead of iframe.mediadelivery.net/embed/</p>
<!--kg-card-begin: html-->
<h3>If you&#x2019;re using custom CSS or JavaScript</h3>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>
  If your player uses custom CSS or JavaScript via the 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">Custom HTML head</code> 
  feature, you&#x2019;ll need to migrate the 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">&lt;head&gt;</code> 
  markup to work with the new player.
</p>
<!--kg-card-end: html-->
<p>The new player uses a different structure and control elements, meaning CSS selectors or scripts targeting the legacy player may no longer work as expected.</p><p>We&#x2019;ve prepared a migration guide that explains how to update common customizations and map legacy selectors to the new player components.</p><p>What you&#x2019;ll need to do:</p>
<!--kg-card-begin: html-->
<ul>
  <li>
    <strong>Search your custom snippet</strong> for 
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">plyr</code>, 
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">.plyr__</code>, 
    and 
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">data-plyr</code>
  </li>
  <li><strong>Replace selectors</strong> using the mapping table in the migration guide</li>
  <li>
    <strong>Search for jQuery usage</strong> 
    (<code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">$(</code>, 
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">jQuery</code>) 
    and rewrite to vanilla JavaScript
  </li>
  <li><strong>Verify on desktop and mobile</strong>, especially controls and captions</li>
</ul>
<!--kg-card-end: html-->
<p>For detailed instructions and selector mappings, follow the complete migration guide <a href="https://docs.bunny.net/stream/custom-head-html-migration-guide" rel="noopener noreferrer">here</a>.</p><h2 id="what%E2%80%99s-under-the-hood-and-where-the-ecosystem-is-heading">What&#x2019;s under the hood and where the ecosystem is heading</h2><p>The new Bunny Stream player is built using <strong>Web Components</strong> and the open-source <a href="https://www.media-chrome.org/">Media Chrome</a> project.</p>
<!--kg-card-begin: html-->
<p>
  Media Chrome provides a set of composable HTML elements that represent common media controls. Elements such as
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">media-controller</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">media-play-button</code>,
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">media-time-range</code>
  can be combined to build a player interface directly in the DOM.
  <br><br>
  Playback itself still relies on the native
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">&lt;video&gt;</code>
  element. Media Chrome components are layered on top of it to provide the user interface.
</p>
<!--kg-card-end: html-->
<p>This HTML-first approach offers a few advantages:</p>
<!--kg-card-begin: html-->
<ul>
  <li><strong>More flexible customization</strong> using standard HTML and CSS</li>
  <li><strong>Improved accessibility</strong> through semantic control elements</li>
  <li><strong>More predictable control structure</strong> for styling and scripting</li>
</ul>
<!--kg-card-end: html-->
<p>This direction also reflects a broader shift happening across the web video ecosystem, with player architectures increasingly moving toward composable, HTML-first approaches.</p><p>By building the Bunny Stream player on Media Chrome, we&#x2019;re aligning with this evolution while keeping the player flexible and easier to extend over time.</p><h2 id="try-the-new-bunny-stream-player">Try the new Bunny Stream player</h2><p>The new Bunny Stream player is now available to everyone and will be used by default for all newly created video libraries.</p><p>If you&#x2019;re already using Bunny Stream, upgrading existing libraries is straightforward. Most libraries can switch to the new player with a single toggle or API call, while customized players can be migrated by following the guide linked above.</p><p>We recommend upgrading when convenient, so you can take advantage of the improvements in the new player and upcoming features.</p><h2 id="references">References</h2><ul><li><a href="https://docs.bunny.net/stream/player" rel="noopener noreferrer">Stream player docs</a></li><li><a href="https://docs.bunny.net/api-reference/core/stream-video-library/update-video-library#response-player-version" rel="noopener noreferrer">API reference</a></li><li><a href="https://docs.bunny.net/stream/custom-head-html-migration-guide" rel="noreferrer">Custom head HTML migration guide</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Skip the setup with full-stack templates on Magic Containers]]></title><description><![CDATA[Templates for Magic Containers wire all of that up before you start. The setup shouldn't slow you down, and bunny.net shouldn’t just be the CDN you bolt on at the end; it should be where you build from the start.]]></description><link>https://bunny.net/blog/skip-the-setup-with-full-stack-templates-on-magic-containers/</link><guid isPermaLink="false">69b0121a160dc403fbfceebd</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jamie Barton]]></dc:creator><pubDate>Tue, 10 Mar 2026 13:37:07 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Bunny-Magic-Containers-Templates.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Bunny-Magic-Containers-Templates.png" alt="Skip the setup with full-stack templates on Magic Containers"><p>You already know what you want to build. Finding the Docker image isn&#x2019;t the problem&#x2026; it&#x2019;s everything after: the database, the volumes, the environment variables, and the networking. That&#x2019;s what turns an afternoon into a weekend.</p><p>Templates for Magic Containers wire all of that up before you start. The setup shouldn&apos;t slow you down, and bunny.net shouldn&#x2019;t just be the CDN you bolt on at the end; it should be where you build from the start.</p><h2 id="introducing-templates-for-magic-containers">Introducing templates for Magic Containers</h2><p>The templates directory is a collection of pre-built full-stack configurations for Magic Containers. Each template comes with everything already configured:</p><ul><li>Application container</li><li>Database sidecar</li><li>Persistent volume</li><li>Environment variables</li><li>Internal networking</li><li>CDN endpoints</li></ul><p>You choose a template, review the configuration, adjust any settings, and deploy. If you&#x2019;ve been meaning to try Magic Containers but aren&#x2019;t sure how to configure everything, templates make it easier to see how it all fits together.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Magic-Containers-Templates.webp" class="kg-image" alt="Skip the setup with full-stack templates on Magic Containers" loading="lazy" width="1984" height="1374" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Magic-Containers-Templates.webp 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/Magic-Containers-Templates.webp 1000w, https://bunny.net/blog/content/images/size/w1600/2026/03/Magic-Containers-Templates.webp 1600w, https://bunny.net/blog/content/images/2026/03/Magic-Containers-Templates.webp 1984w" sizes="(min-width: 720px) 720px"></figure><p>Whether you&#x2019;re shipping a workflow backend, a content platform, or a data API, the directory covers the stacks developers actually reach for:</p><ul><li><a href="https://dash.bunny.net/magic-containers/templates/n8n-with-psql-0" rel="noopener noreferrer">n8n with Postgres</a> for workflow automation</li><li><a href="https://dash.bunny.net/magic-containers/templates/umami-psql-0" rel="noopener noreferrer">Umami Analytics</a> with Postgres for privacy-focused web analytics</li><li><a href="https://dash.bunny.net/magic-containers/templates/wordpress-0" rel="noopener noreferrer">WordPress with MariaDB</a></li><li><a href="https://dash.bunny.net/magic-containers/templates/nextjs-with-psql-0" rel="noopener noreferrer">Next.js with Postgres</a></li><li><a href="https://dash.bunny.net/magic-containers/templates/rails-0" rel="noopener noreferrer">Rails</a>, <a href="https://dash.bunny.net/magic-containers/templates/laravel-with-mariadb-0" rel="noopener noreferrer">Laravel</a>, <a href="https://dash.bunny.net/magic-containers/templates/django-with-redis-0" rel="noopener noreferrer">Django</a>, <a href="https://dash.bunny.net/magic-containers/templates/flask-0" rel="noopener noreferrer">Flask</a>, <a href="https://dash.bunny.net/magic-containers/templates/go-api-with-redis-0" rel="noopener noreferrer">Go</a> with databases</li><li><a href="https://dash.bunny.net/magic-containers/templates/hono-api-with-duckdb-0" rel="noopener noreferrer">Hono with DuckDB</a></li><li><a href="https://dash.bunny.net/magic-containers/templates/vite-react-nginx-0" rel="noopener noreferrer">Vite + React with Nginx</a></li></ul><p>We are adding more regularly. If there is a stack you would like to see or one you want to contribute, <a href="https://discord.com/invite/bunnynet" rel="noopener noreferrer">join us on Discord</a>.</p><h2 id="what-a-template-gives-you">What a template gives you</h2><p>Setting up from scratch means creating the application, pulling containers from a registry, wiring up a database, attaching volumes, configuring environment variables, and exposing endpoints. It&apos;s not hard; it&#x2019;s just friction.</p><p>A template removes that friction. The containers are already wired together, storage is attached, environment variables are set, and the database is reachable over localhost. Review the config, adjust what you need, and deploy. The whole stack comes up together.</p><h2 id="eject-to-github">Eject to GitHub</h2><p>Templates are a starting point, not a commitment. If you want full control over the code, eject the project to your own GitHub repository and take it from there.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Magic-Containers-Template-Creation.webp" class="kg-image" alt="Skip the setup with full-stack templates on Magic Containers" loading="lazy" width="1424" height="192" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Magic-Containers-Template-Creation.webp 600w, https://bunny.net/blog/content/images/size/w1000/2026/03/Magic-Containers-Template-Creation.webp 1000w, https://bunny.net/blog/content/images/2026/03/Magic-Containers-Template-Creation.webp 1424w" sizes="(min-width: 720px) 720px"></figure>
<!--kg-card-begin: html-->
<p>
  Once ejected, the repo is yours. You can modify the <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">Dockerfile</code>, change the stack, and control the code.
</p>
<!--kg-card-end: html-->
<figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/03/Magic-Containers-GitHub-Ejection.webp" class="kg-image" alt="Skip the setup with full-stack templates on Magic Containers" loading="lazy" width="960" height="882" srcset="https://bunny.net/blog/content/images/size/w600/2026/03/Magic-Containers-GitHub-Ejection.webp 600w, https://bunny.net/blog/content/images/2026/03/Magic-Containers-GitHub-Ejection.webp 960w" sizes="(min-width: 720px) 720px"></figure><p>Push a change, and your image builds through GitHub Container Registry. Magic Containers redeploys the updated image automatically with the GitHub Action that&#x2019;s preconfigured in each template.</p><p>You get the same CI/CD workflow you would configure yourself, without the initial boilerplate.</p><h2 id="where-we%E2%80%99re-heading">Where we&#x2019;re heading</h2><p>Templates solve the setup problem, but the bigger goal is making bunny.net a platform you can build on, share from, and automate.</p><p>We&#x2019;re building the Magic Deploy button, a one-click deploy button that lets you add to any repository README or project page. Anyone can click it and deploy the stack directly to bunny.net, even without an account. The full configuration is prewired and deploys directly from source code.</p><p>We&#x2019;re also building a CLI for the bunny.net developer toolkit. It&apos;s one of many developer tools designed to help you manage databases, edge scripts, storage buckets, and Magic Containers apps from the terminal.</p><p>The goal is to make bunny.net easier to build on. Templates when you want a head start, eject when you want control, one-click deploys when you want to share, and a CLI to tie it all together.</p><h2 id="try-it">Try it</h2><p>Templates are available now in the bunny.net dashboard. <a href="https://dash.bunny.net/magic-containers/templates">Choose a template</a>, deploy a full-stack app in seconds, and eject to GitHub when you&#x2019;re ready to take it further. This is what building on bunny.net looks like, and we&#x2019;re just getting started.</p><p>If there is a template you would like to see added or one you want to contribute, <a href="https://discord.com/invite/bunnynet">join the discussion in our Discord</a>.</p>]]></content:encoded></item><item><title><![CDATA[Migrating from Heroku to Magic Containers]]></title><description><![CDATA[On February 6, 2026, Heroku announced that it is entering a sustaining engineering model. No new features, no new enterprise contracts. If you’re starting to think about what comes next, Magic Containers offers a straightforward migration path]]></description><link>https://bunny.net/blog/migrating-from-heroku-to-magic-containers/</link><guid isPermaLink="false">69a93767160dc403fbfcee58</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jamie Barton]]></dc:creator><pubDate>Thu, 05 Mar 2026 08:45:03 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Heroku-Magic-Containers-Migration.png" medium="image"/><content:encoded><![CDATA[
<!--kg-card-begin: html-->
<img src="https://bunny.net/blog/content/images/2026/03/Heroku-Magic-Containers-Migration.png" alt="Migrating from Heroku to Magic Containers"><p>
  I&#x2019;ve been a huge fan of Heroku since the early days. They were true pioneers of platform as a service,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">git push heroku master</code>
  was magic when it first appeared, and they made building scalable web apps and services genuinely easy at a time when the alternative was wrestling with EC2 instances and shell scripts.
</p>
<!--kg-card-end: html-->
<p>A lot of us built our first production apps on Heroku, and the developer experience they created shaped how an entire generation thinks about deployment.</p><p>On February 6, 2026, Heroku announced that it is entering a sustaining engineering model. No new features, no new enterprise contracts. If you&#x2019;re starting to think about what comes next, Magic Containers offers a straightforward migration path.</p><p>If you&#x2019;ve been building twelve-factor apps on Heroku environment-based config, stateless processes, and backing services as attached resources, you&#x2019;ll find that most of those principles translate directly to containers. The deployment model is different, but the thinking is the same.</p><h2 id="how-heroku-concepts-map-to-magic-containers">How Heroku concepts map to Magic Containers</h2><p>If you&apos;re familiar with Heroku, here&apos;s how the terminology translates:</p>
<!--kg-card-begin: html-->
<table style="border: 1px solid; max-width: 600px">
  <thead>
    <tr style="
        font-weight: 700;
        text-align: left;
        background: #223c6a;
        color: white;
      ">
      <th style="
          padding: 12px 14px;
          background: rgba(255, 255, 255, 0.03);
          border-bottom: 1px solid;
          border-right: 1px solid;
        ">
        Heroku
      </th>
      <th style="padding: 12px 14px; border-bottom: 1px solid">
        Magic Containers
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="
          padding: 12px 14px;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        App
      </td>
      <td style="padding: 12px 14px; vertical-align: middle">Application</td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Dyno (web/worker)
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Container
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Buildpack
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Docker image
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Add-on (e.g. Heroku Postgres)
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Additional container in the same app
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        <code style="
            padding: 2px 6px;
            border-radius: 6px;
            color: rgba(248, 113, 113, 0.95);
            border: 1px solid rgba(239, 68, 68, 0.2);
            white-space: nowrap;
          ">Procfile</code>
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Container image entrypoint
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Config Vars
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Environment variables
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        <code style="
            padding: 2px 6px;
            border-radius: 6px;
            color: rgba(248, 113, 113, 0.95);
            border: 1px solid rgba(239, 68, 68, 0.2);
            white-space: nowrap;
          ">heroku.yml</code>
        <span style="opacity: 0.9"> / Dockerfile deploy</span>
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Docker image from Docker Hub or GitHub
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Dyno scaling
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Autoscaling (min/max instances per region)
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Pipeline (staging/production)
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        Separate applications per environment
      </td>
    </tr>
    <tr>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          border-right: 1px solid;
          vertical-align: middle;
        ">
        Region (US/EU)
      </td>
      <td style="
          padding: 12px 14px;
          border-top: 1px solid;
          vertical-align: middle;
        ">
        40+ regions worldwide
      </td>
    </tr>
  </tbody>
</table>
<!--kg-card-end: html-->
<h3 id="key-differences">Key differences</h3>
<!--kg-card-begin: html-->
<ul>
  <li>
    <strong>No buildpacks, just Docker images:</strong> Heroku uses buildpacks to detect your language and build your app automatically. Magic Containers runs standard Docker images, giving you full control over your runtime, dependencies, and build process. You can deploy any public or private image from Docker Hub or GitHub Container Registry in any language or framework.
  </li>

  <li>
    <strong>Multi-container composition with persistent storage:</strong> Heroku apps typically run as a single dyno, with databases provided as separate add-ons connected over the network. Magic Containers allows multiple containers within the same application that communicate over 
    <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">localhost</code>.
    This lets you run your app alongside its database without an external hosted database service. Persistent volumes provide durable storage so database files, uploads, and application state survive redeployments and restarts.
  </li>

  <li>
    <strong>Low-level networking:</strong> Heroku primarily provides HTTP routing in the US or the EU. Magic Containers supports TCP and UDP via global Anycast in addition to HTTP, enabling workloads such as DNS servers, game servers, VPN endpoints, or custom protocols.
  </li>

  <li>
    <strong>No <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">git push</code> deploys:</strong> Instead of pushing code directly, you build a Docker image locally or in CI, push it to a registry, and select it in the Magic Containers dashboard. This fits naturally into GitHub Actions or any CI/CD pipeline.
  </li>

  <li>
    <strong>Flexible autoscaling and provisioning:</strong> Heroku restricts autoscaling mainly to web dynos and higher-tier plans. Magic Containers autoscales by default and allows customization of scaling behavior and replica counts.
  </li>
</ul>
<!--kg-card-end: html-->
<h3 id="migration-steps">Migration steps</h3><p><strong>1. Containerize your app</strong></p>
<!--kg-card-begin: html-->
<p>
  If you already have a <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">Dockerfile</code>, you&apos;re ready. If not, create one for your app. Most frameworks have well-documented Docker setups.
</p>
<!--kg-card-end: html-->
<p>Here&apos;s a minimal example for a Node.js app:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<p><code style="color:#779FC9;">FROM</code> node:20-alpine</p>

<p><code style="color:#779FC9;">WORKDIR</code> /app
  <br>
<code style="color:#779FC9;">COPY</code> package*.json ./</p>

<p><code style="color:#779FC9;">RUN</code> npm ci --production
<br>
<code style="color:#779FC9;">COPY</code> . .</p>

<p><code style="color:#779FC9;">EXPOSE</code> 3000</p>

<p><code style="color:#779FC9;">CMD</code> [<code style="color:#B9D55E;">&quot;node&quot;</code>, <code style="color:#B9D55E;">&quot;server.js&quot;</code>]</p>
</div>
<!--kg-card-end: html-->
<p></p><p>On Heroku, your Procfile might define multiple process types like web and worker. With Docker, each process type becomes its own image (or the same image with a different command). For example, a worker that processes background jobs:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<p><code style="color:#C7989E;">FROM</code> node<code style="color:#E9BC4F;">:</code><code style="color:#C7989E;">20</code>-alpine</p>

<p><code style="color:#C7989E;">WORKDIR</code> <code style="color:#E9BC4F;">/</code>app
<br>
<code style="color:#C7989E;">COPY</code> <code style="color:#779FC9;">package</code><code style="color:#E9BC4F;">*</code>.json .<code style="color:#E9BC4F;">/</code></p>

<p><code style="color:#C7989E;">RUN</code> npm ci <code style="color:#E9BC4F;">--</code>production
<br>
<code style="color:#C7989E;">COPY</code> . .</p>

<p><code style="color:#C7989E;">CMD</code> [<code style="color:#B9D55E;">&quot;node&quot;</code>, <code style="color:#B9D55E;">&quot;worker.js&quot;</code>]</p>
</div>
<!--kg-card-end: html-->
<p></p>
<!--kg-card-begin: html-->
<p>
  It&#x2019;s also possible to use a single <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">Dockerfile</code> and override the command per container (common with Go), if that&#x2019;s your thing. On Magic Containers, you&apos;d add both as separate containers in the same application: the web container with a CDN endpoint, and the worker container with no endpoint. They share <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">localhost</code>, so your worker can connect to the same database and Redis instance as your web process.
</p>
<!--kg-card-end: html-->
<p><strong>2. Push your image to a registry</strong></p><p>Build and push your image to Docker Hub or GitHub Container Registry:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

<code style="color:#E9BC4F;">docker</code> build -t yourusername/myapp:latest .
<br>
<code style="color:#E9BC4F;">docker</code> push yourusername/myapp:latest

</div>
<!--kg-card-end: html-->
<p></p><p>Then connect your registry in the Magic Containers dashboard under <strong>Image Registries</strong>.</p><p><strong>3. Create your application</strong></p><p>In the Magic Containers dashboard, click <strong>Add App</strong> and choose your deployment strategy. For stateful apps with a database, choose <strong>Single Region</strong>. For stateless apps, <strong>Magic deployment</strong> will distribute your app globally.</p><p><strong>4. Add your containers</strong></p>
<!--kg-card-begin: html-->
<p>
  Add your app container, selecting the image you just pushed. Set your environment variables. These are the same config vars you had in Heroku, such as
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">DATABASE_URL</code>,
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">SECRET_KEY</code>,
  and
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">PORT</code>.
</p>
<!--kg-card-end: html-->

<!--kg-card-begin: html-->
<p>
  If you were using Heroku Postgres, add a PostgreSQL container in the same application. Since containers in the same app share 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">localhost</code>, update your database connection to point to 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">localhost</code> instead of the Heroku Postgres hostname.
</p>
<!--kg-card-end: html-->
<p><strong>5. Expose your app</strong></p><p>Add a <strong>CDN</strong> endpoint pointing to your app&apos;s container port. This gives you a public URL with automatic HTTPS, no need to configure SSL certificates. You can also use <strong>Anycast</strong> for non-HTTP protocols like TCP or WebSocket traffic.</p><p><strong>6. Export and import your data</strong></p><p>Export your Heroku Postgres database:</p>
<!--kg-card-begin: html-->
<div style="background-color:#1e1e1e; padding:1em; border-radius:6px; font-family:monospace; color:#ffffff;">

heroku pg:backups:capture --app your-app
<br>
heroku pg:backups:download --app your-app

</div>
<!--kg-card-end: html-->
<p></p>
<!--kg-card-begin: html-->
<p>
  Then restore it into your new PostgreSQL container. If your new Postgres is accessible via an Anycast endpoint, you can connect directly with 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">pg_restore</code> 
  or 
  <code style="background-color:#1e1e1e; color:#EB5757; padding:2px 6px; border-radius:4px;">psql</code>.
</p>
<!--kg-card-end: html-->
<h2 id="example-deployments">Example deployments</h2><p>We have step-by-step guides for deploying popular languages, frameworks, and databases on Magic Containers. These include guides for building APIs with:</p><ul><li><a href="https://docs.bunny.net/magic-containers/guides/simple-go-api">Go</a></li><li>Node.js (<a href="https://docs.bunny.net/magic-containers/guides/node-express-api" rel="noopener noreferrer">Express</a> and <a href="https://docs.bunny.net/magic-containers/guides/node-hono-api" rel="noopener noreferrer">Hono</a>)</li><li><a href="https://docs.bunny.net/magic-containers/guides/python-fastapi" rel="noopener noreferrer">Python (FastAPI)</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/php-slim" rel="noopener noreferrer">PHP (Slim)</a></li></ul><p>If you&#x2019;re looking to get started with a popular web framework, you can host those too:</p><ul><li><a href="https://docs.bunny.net/magic-containers/guides/astro" rel="noopener noreferrer">Astro</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/nextjs" rel="noopener noreferrer">Next.js</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/nuxt" rel="noopener noreferrer">Nuxt</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/ruby-on-rails" rel="noopener noreferrer">Ruby on Rails</a></li></ul><p>And databases, standalone or as sidecars to your container apps:</p><ul><li><a href="https://docs.bunny.net/magic-containers/guides/clickhouse" rel="noreferrer">ClickHouse</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/mariadb" rel="noopener noreferrer">MariaDB</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/postgresql" rel="noopener noreferrer">Postgres</a></li><li><a href="https://docs.bunny.net/magic-containers/guides/redis" rel="noopener noreferrer">Redis</a></li></ul><p></p><p>Each guide shows how to configure multi-container apps with databases, <a href="https://bunny.net/blog/magic-containers-now-supports-persistent-volumes-storage-that-survives-restarts-and-redeployments/" rel="noreferrer">persistent volumes</a>, and CDN endpoints.</p><h2 id="you-might-not-need-a-container">You might not need a container</h2><p>Not every Heroku app needs to become a container. bunny.net offers two other products that can replace parts of your stack with less overhead.</p><p><a href="https://docs.bunny.net/scripting" rel="noreferrer"><strong>Edge Scripting</strong></a> is a serverless runtime for JavaScript and TypeScript that runs across bunny.net&#x2019;s network. If your Heroku app is a lightweight API, a webhook handler, or middleware layer, Edge Scripting can replace it without a container at all. It also works as DNS middleware, letting you intercept and modify requests at the edge before they reach your origin. There&#x2019;s no infrastructure to manage, or Dynos to scale.</p><p><a href="https://docs.bunny.net/database" rel="noreferrer"><strong>Bunny Database</strong></a> is a globally distributed, SQLite-compatible database. If you were using Heroku Postgres through a third-party add-on, or your app doesn&#x2019;t need the full power of PostgreSQL, Bunny Database is a simpler alternative that runs close to your code across bunny.net&#x2019;s network.</p><h2 id="getting-started">Getting started</h2><p>Magic Containers is designed to be the kind of platform Heroku was at its best: simple to deploy to, with none of the complexity you don&#x2019;t need. Full flexibility of Docker and a global edge network.</p><p>You bring a container image, set your environment variables, attach storage where you need it, and you&#x2019;re running. No buildpack debugging, no add-on marketplace, no dyno sleep.</p><p>We&#x2019;d love to see what you&#x2019;re building. If you&#x2019;re mid-migration, just getting started, or want to swap notes with others making the same move, come join us on Discord.</p>]]></content:encoded></item><item><title><![CDATA[Magic Containers now supports persistent volumes: storage that survives restarts and redeployments]]></title><description><![CDATA[While autoscaling compute is largely a solved problem in modern PaaS, persistent storage often still means provisioning volumes manually, mounting them to specific machines, and ensuring they stay attached as your app scales or restarts.
]]></description><link>https://bunny.net/blog/magic-containers-now-supports-persistent-volumes-storage-that-survives-restarts-and-redeployments/</link><guid isPermaLink="false">69a7ee2c160dc403fbfcee04</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Wed, 04 Mar 2026 09:32:10 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/03/Magic-Containers-Persistent-Volumes.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/03/Magic-Containers-Persistent-Volumes.png" alt="Magic Containers now supports persistent volumes: storage that survives restarts and redeployments"><p>After bringing <a href="https://bunny.net/blog/meet-bunny-database-the-sql-service-that-just-works/">Bunny Database into public preview</a> last month, we&#x2019;re continuing to build out the bunny.net developer toolkit with a new release for Magic Containers, our simple but powerful app hosting service.</p><p>Let&#x2019;s hop right into the details.</p><h3 id="persistent-volumes-without-the-wiring">Persistent volumes without the wiring</h3><p>While autoscaling compute is largely a solved problem in modern PaaS, persistent storage often still means provisioning volumes manually, mounting them to specific machines, and ensuring they stay attached as your app scales or restarts.</p><p>Even having to think about that isn&apos;t any fun when you just want to build things, so we took great care to ship persistent storage that actually gets out of your way.</p><p>Rather than forcing you to reason in terms of machines, Magic Containers opts for a Docker Compose-style mental model, where your app lives in a pod. A pod may consist of multiple containers that share networking and resources, now including persistent volumes.</p><p>Here&#x2019;s everything you need to know about this new capability:</p><ul><li><strong>Auto-attach and detach:</strong> When your app scales and new replicas are started, new volumes are provisioned and attached automatically. When replicas scale down, their volumes detach but persist so they&#x2019;re ready to reattach when needed, keeping all data intact.</li><li><strong>No data duplication between replicas:</strong> Each pod gets its own dedicated volume. When your app is replicated across regions, every replica runs with its own storage.</li><li><strong>Encryption:</strong> Every volume is AES-encrypted by default to keep all data safe.</li><li><strong>Limits:</strong> Up to 100 GB per volume, with up to two volumes per app (pod).</li><li><strong>Cost:</strong> $0.10 per GB per month, based on the allocated (provisioned) volume size.<br></li></ul><p>So the idea is that persistent volumes in Magic Containers are set-and-forget. You just add a volume to your app and it&#x2019;s automatically provisioned and managed, without you having to worry about volume behavior or any wiring.</p><p>In this demo, we set up a single-replica Redis instance and a Go API on Magic Containers using a persistent volume:</p>
<!--kg-card-begin: html-->
<div style="position:relative;padding-top:56.25%;"><iframe src="https://iframe.mediadelivery.net/embed/426067/afaf16c0-5271-4027-af78-acbd8e975dd7?autoplay=false&amp;loop=true&amp;muted=false&amp;preload=true&amp;responsive=true" loading="lazy" style="border:0;position:absolute;top:0;height:100%;width:100%;" allow="accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;" allowfullscreen="true"></iframe></div>
<!--kg-card-end: html-->
<h3 id="when-to-use-persistent-volumes">When to use persistent volumes</h3><p>Magic Containers works best for apps and services you want running 24/7, such as:</p><ul><li><strong>Web apps:</strong> always-on APIs, backends, and server-rendered apps</li><li><strong>Workers and automation:</strong> long-running jobs and internal services</li><li><strong>Real-time and TCP/UDP apps:</strong> game servers and services with persistent connections</li></ul><p>Now that Magic Containers supports persistent volumes, you can have durable data right next to your code. This is useful for a number of things:</p><ul><li><strong>Caches and generated assets:</strong> image caches, build caches, template compilation, model files</li><li><strong>Background processing and workers:</strong> file processing, PDF generation, media transcoding, temporary spooling before uploading to object storage</li><li><strong>Stateful single-instance services:</strong> DBs such as Redis and Postgres, or internal tools</li></ul><p>For databases and caches that expect a single writable disk, run with 1 replica per volume. This is because each replica gets its own volume, so running multiple replicas of a stateful service could lead to state inconsistency. If you need your data replicated or distributed across regions, use <a href="https://docs.bunny.net/storage" rel="noreferrer">Bunny Storage</a> or <a href="https://docs.bunny.net/database" rel="noreferrer">Bunny Database</a> instead.</p><h3 id="what-will-you-build">What will you build?</h3><p>We can&#x2019;t wait to see what stateful apps you&#x2019;ll build with Magic Containers. <a href="https://dash.bunny.net/auth/login" rel="noreferrer">Log in or sign up to the dashboard</a> to try out persistent volumes and let us know what you think on <a href="https://discord.com/invite/bunnynet" rel="noreferrer">Discord</a>.</p><p>This release is another addition to the bunny.net developer toolkit, and we don&#x2019;t stop shipping. Stay tuned for more updates later this month.</p><p>Happy building!<br></p>]]></content:encoded></item><item><title><![CDATA[Meet Bunny Database: the SQL service that just works]]></title><description><![CDATA[Not every project needs Postgres, and that’s okay. Sometimes you just want a simple, reliable database that you can spin up quickly and build on, without worrying it’ll hit your wallet like a EC2.]]></description><link>https://bunny.net/blog/meet-bunny-database-the-sql-service-that-just-works/</link><guid isPermaLink="false">697c7dba160dc403fbfced4c</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Tue, 03 Feb 2026 09:37:09 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/02/Bunny-Dataase-SQL-service-that-just-works.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/02/Bunny-Dataase-SQL-service-that-just-works.png" alt="Meet Bunny Database: the SQL service that just works"><p>Don&#x2019;t want to babysit your app database on a VM but not willing to pay the DBaaS tax either? We&apos;re building a third way.</p><p>Today, we&#x2019;re launching Bunny Database as a public preview: a SQLite-compatible managed service that spins down when idle, keeps latency low wherever your users are, and doesn&#x2019;t cost a fortune.</p><h2 id="so-what%E2%80%99s-the-deal-with-database-services-in-2026">So what&#x2019;s the deal with database services in 2026?</h2><p>It&#x2019;s become clear by now that the DBaaS platforms that garnered the love of so many devs are all going upmarket. Removing or dumbing down free tiers, charging for unused capacity, charging extra for small features, or bundling them in higher tiers &#x2014; you already know the drill.</p><p>Hard to blame anyone for growing their business, but it doesn&#x2019;t feel right when these services stop making sense for the very people who helped popularize them in the first place.</p><p>So where does that leave you?</p><h2 id="like-sqlite-but-for-the-web">Like SQLite, but for the web</h2><p>Not every project needs Postgres, and that&#x2019;s okay. Sometimes you just want a simple, reliable database that you can spin up quickly and build on, without worrying it&#x2019;ll hit your wallet like an EC2.</p><p>That&#x2019;s what we built Bunny Database for.</p><p>What you get:</p><ul><li><strong>One-click deployment</strong>: just name your database and go, no config needed</li><li><strong>Language-specific tooling</strong>: SDKs for TS/JS, Go, Rust, and .NET help you handle the boring bits</li><li><strong>Low latency anywhere</strong>: replication regions let you serve reads close to your users</li><li><strong>41 regions worldwide</strong>: choose between automatic, single-region, and multi-region deployment</li><li><strong>Works over HTTP</strong>: wire up anything you&#x2019;d like</li><li><strong>Database editor</strong>: insert data or run queries on the spot</li><li><strong>Metrics</strong>: instant visibility into reads, writes, storage, and latency</li><li><strong>Affordable, pay-as-you-go pricing</strong>: only pay for what you use, but without the serverless tax</li></ul><p>Get the full tour including how to connect Bunny Database to your app in this quick demo from our DX Engineer, Jamie Barton:</p>
<!--kg-card-begin: html-->
<div style="position:relative;padding-top:56.25%;"><iframe src="https://iframe.mediadelivery.net/embed/426067/458d9f76-9999-43e0-8479-ed5f92788b9b?autoplay=false&amp;loop=true&amp;muted=false&amp;preload=true&amp;responsive=true" loading="lazy" style="border:0;position:absolute;top:0;height:100%;width:100%;" allow="accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;" allowfullscreen="true"></iframe></div>
<!--kg-card-end: html-->
<h2 id="why-care-about-database-latency-anyway">Why care about database latency anyway?</h2><p>You probably optimize the heck out of your frontend, APIs, and caching layers, all for the sake of delivering an experience that feels instant to your users. But when your database is far away from them, round-trip time starts to add noticeable latency.</p><p>The usual fix is to introduce more caching layers, denormalized reads, or other workarounds. That&#x2019;s obviously no fun.</p><p>And when you think about it, devs end up doing this because the popular DBaaS platforms are usually either limited, complex, or too costly when it comes to multi-region deployments. So what looks like a caching problem is actually a data locality issue.</p><p>OK, but how bad can it really be?</p><p>To find out, we ran a read latency benchmark and measured p95 latency in Bunny Database.</p><p>We picked a number of regions across the world and compared round-trip time for client locations ever farther away from the database in:</p><ul><li>a single-region setup,</li><li>with replication regions enabled.</li></ul><p>Turns out serving reads close to clients reduced latency by up to 99%.</p><p>Check out the full write-up on the benchmark setup and results <a href="https://bunny.net/blog/how-database-location-affects-far-away-users-benchmarking-read-latency-in-bunny-database/">here</a>.</p><p></p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-Database-Latency-Graph.webp" class="kg-image" alt="Meet Bunny Database: the SQL service that just works" loading="lazy" width="1330" height="986" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-Database-Latency-Graph.webp 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-Database-Latency-Graph.webp 1000w, https://bunny.net/blog/content/images/2026/01/Bunny-Database-Latency-Graph.webp 1330w" sizes="(min-width: 720px) 720px"></figure><p>While this definitely matters most to apps with global users, data locality does apply to everyone. With Bunny Database, you don&#x2019;t have to stick to major data center locations and compensate with caching workarounds any more. Instead, you get a lot of flexibility to set up regions in an intuitive interface and it&#x2019;s easy to switch things up as your requirements change.</p><p>Choose between 3 deployment types when creating a database:</p><ul><li><strong>Automatic region selection</strong> gives you one-click deployment with minimal latency. Bunny Database will select regions for you based on your IP address (you can check and tweak the selection in settings later).</li><li><strong>Single-region deployment</strong> lets you pick one of 41 regions available worldwide (check the full list <a href="https://docs.bunny.net/magic-containers/regions" rel="noopener noreferrer">here</a>).</li><li><strong>Manual region selection</strong> gives you custom multi-region setup, where you can freely pick regions that make the most sense for your audience.</li></ul><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-Database-regions.webp" class="kg-image" alt="Meet Bunny Database: the SQL service that just works" loading="lazy" width="1524" height="1328" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-Database-regions.webp 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-Database-regions.webp 1000w, https://bunny.net/blog/content/images/2026/01/Bunny-Database-regions.webp 1524w" sizes="(min-width: 720px) 720px"></figure><p>All of this lets you start wherever you&#x2019;d like and add regions as needed, without re-architecting your app.</p><h2 id="usage-based-pricing-but-without-the-serverless-tax">Usage-based pricing, but without the serverless tax</h2><p>In the database world, capacity-based pricing gives you some predictability. But no one likes to pay for unused capacity, right?</p><p>Serverless, on the other hand, is supposed to be cost-efficient, yet can rack up bills quickly, especially when the DBaaS charges significant markups on top of already pricey compute.</p><p>We don&#x2019;t do hyperscalers, though, so we can charge a fair price for Bunny Database in a usage-based model.</p><ul><li>Reads: <strong>$0.30 per billion rows</strong></li><li>Writes: <strong>$0.30 per million rows</strong></li><li>Storage: <strong>$0.10 per GB per active region</strong> (monthly)</li><li>When not getting requests, Bunny Database only incurs storage costs. One primary region is charged continuously, while read replicas only add storage costs when serving traffic (metered by the hour)</li><li>Your usage is charged continuously (pay-as-you-go) and invoiced monthly</li></ul><p>During the public preview phase, Bunny Database is free.</p><h2 id="wait-what-does-%E2%80%9Csqlite-compatible%E2%80%9D-actually-mean">Wait, what does &#x201C;SQLite-compatible&#x201D; actually mean?</h2><p>Bunny Database wouldn&#x2019;t be possible without libSQL, the open-source, open-contribution fork of SQLite created by Turso.</p><p>We run Bunny Database on our own fork of libSQL, which gives us the freedom to integrate it tightly with the bunny.net platform and handle the infrastructure and orchestration needed to run it as a managed, multi-region service.</p><p>What does this mean for Bunny Database&#x2019;s upstream feature parity with libSQL and SQLite, respectively?</p><p>The short answer is that we don&#x2019;t currently promise automatic or complete feature parity with either upstream libSQL or the latest SQLite releases.</p><p>While libSQL aims to stay compatible with SQLite&#x2019;s API and file format, it doesn&#x2019;t move in lockstep with upstream SQLite. We wouldn&#x2019;t expect otherwise, especially as Turso has shifted focus from libSQL toward a long-term rewrite of SQLite in Rust.</p><p>For Bunny Database, this means that compatibility today is defined by the libSQL version we&#x2019;re built on, rather than by chasing every upstream SQLite or libSQL change as it lands. We haven&#x2019;t pulled in any upstream changes yet, and we don&#x2019;t currently treat upstream parity as an automatic goal.</p><p>That&#x2019;s intentional. Our focus so far has been on making Bunny Database reliable and easy to operate as a service. We think bringing in upstream changes only makes sense when they clearly improve real-world use cases, not just to tick a parity checkbox.</p><p>If there are specific libSQL features you&#x2019;d like to see exposed in Bunny Database, or recent SQLite features you&#x2019;d want us to pull in, we&#x2019;d love to hear about it. <a href="https://discord.com/invite/bunnynet" rel="noopener noreferrer">Join our Discord</a> to discuss your use cases and help shape the roadmap!</p><h2 id="what%E2%80%99s-ahead-for-bunny-database">What&#x2019;s ahead for Bunny Database</h2><p>Speaking of the roadmap, we don&#x2019;t stop cooking. Here&#x2019;s what&#x2019;s coming up next:</p><ul><li>Automatic backups</li><li>Database file import/export</li><li>Auto-generated, schema-aware API with type-safe SDKs</li></ul><p>There&#x2019;s even more to come, but it&#x2019;s too soon to spill the beans yet, especially while we&#x2019;re in public preview. We&#x2019;d love to hear your feedback, so we can shape what ships next together.</p><h2 id="more-tools-to-build-with">More tools to build with</h2><p>Bunny Database works standalone and fits right into your stack via the SDKs (or you can hook up anything using the HTTP API). But it also plays nicely with <a href="https://docs.bunny.net/docs/edge-scripting-overview">Bunny Edge Scripting</a> and <a href="https://docs.bunny.net/docs/magic-containers-overview">Bunny Magic Containers</a>.</p><p>To connect your database to an Edge Script or a Magic Containers app, simply go to the Access tab of the chosen database and click <strong>Generate Tokens</strong> to create new access credentials for it.</p><p>Once they&#x2019;re generated, you&#x2019;ll get two paths to choose from:</p><ul><li><strong>Click Add Secrets to an Edge Script</strong> and select the one you&#x2019;d like to connect from the list. You&#x2019;ll also need to import the libSQL TypeScript client and use the provided code snippet to connect it to your database.</li><li><strong>Click Add Secrets to Magic Container App</strong> and select the one you&#x2019;d like to connect from the list. You&#x2019;ll also need to connect to the database from your app using one of the client libraries or the HTTP API.</li></ul><p>After you complete the setup, the database URL and access token will be available as environment variables in your script or app. Use them to connect to your database:</p>
<!--kg-card-begin: html-->
<div style="background-color: #2b2b2b; padding: 1em; border-radius: 6px; font-family: monospace; color: #ffffff;">

  <code style="color: #82aaff;">import</code> 
  <code style="color: #ffffff;">{</code> 
  <code style="color: #ffffff;">createClient</code> 
  <code style="color: #ffffff;">}</code> 
  <code style="color: #82aaff;">from</code> 
  <code style="color: #c3e88d;">&quot;@libsql/client/web&quot;</code><code style="color: #ffffff;">;</code>

  <br><br>

  <code style="color: #82aaff;">const</code> 
  <code style="color: #ffffff;">client</code> 
  <code style="color: #bc9843;">=</code> 
  <code style="color: #d3d48c;">createClient</code><code style="color: #ffffff;">({</code><br>
  &#xA0;&#xA0;<code style="color: #ffffff;">url</code><code style="color: #bc9843;">:</code> 
  <code style="color: #ffffff;">process.env.</code><code style="color: #f07178;">DB_URL</code><code style="color: #ffffff;">,</code><br>
  &#xA0;&#xA0;<code style="color: #ffffff;">authToken</code><code style="color: #bc9843;">:</code> 
  <code style="color: #ffffff;">process.env.</code><code style="color: #f07178;">DB_TOKEN</code><br>
  <code style="color: #ffffff;">});</code>

  <br><br>

  <code style="color: #82aaff;">const</code> 
  <code style="color: #ffffff;">result</code> 
  <code style="color: #bc9843;">=</code> 
  <code style="color: #ffffff;">client</code><code style="color: #ffffff;">.</code><code style="color: #d3d48c;">execute</code><code style="color: #ffffff;">(</code><code style="color: #c3e88d;">&quot;SELECT * FROM users&quot;</code><code style="color: #ffffff;">);</code>

</div>

<!--kg-card-end: html-->
<p>You can find more detailed, step-by-step integration instructions in the docs:</p><ul><li><a href="https://docs.bunny.net/database/connect/scripting" rel="noopener noreferrer">Connecting an Edge Script to Bunny Database</a></li><li><a href="https://docs.bunny.net/database/connect/magic-containers" rel="noopener noreferrer">Connecting a Magic Containers app to Bunny Database</a></li></ul><h2 id="hop-on-board">Hop on board</h2><p>We can&#x2019;t wait to see what you&#x2019;ll build with Bunny Database and what you think of it. During the public preview phase, you get 50 databases per user account, each capped at 1 GB, but we hope this should be more than enough for lots of fun projects.</p><p>Just sign in to the bunny.net dashboard to get started. Happy building!</p>]]></content:encoded></item><item><title><![CDATA[How database location affects far-away users: benchmarking read latency in Bunny Database]]></title><description><![CDATA[To find out, we measured p95 latency in Bunny Database across increasing distances between the database and client locations. ]]></description><link>https://bunny.net/blog/how-database-location-affects-far-away-users-benchmarking-read-latency-in-bunny-database/</link><guid isPermaLink="false">697c88aa160dc403fbfced8e</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Tue, 03 Feb 2026 09:36:49 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/02/Bunny-Database-latency-benchmark.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/02/Bunny-Database-latency-benchmark.png" alt="How database location affects far-away users: benchmarking read latency in Bunny Database"><p>You can spend a lot of time squeezing milliseconds out of your frontend, APIs, and caching layers. But if your database sits far away from your users, every request still pays the cost of a long round trip.</p><p>When that happens, devs usually reach for workarounds: more caching, denormalized reads, background sync jobs, or other techniques to hide the latency. These approaches work, but they add complexity quickly.</p><p>The underlying issue is simpler. For most applications, database reads are still centralized in a single region, and distance is unavoidable. This isn&#x2019;t really a caching problem &#x2014; it&#x2019;s a data locality problem.</p><p>So the obvious question is: <strong>how bad does it actually get as users move farther away from the database?</strong></p><p>To find out, we measured p95 latency in <a href="https://docs.bunny.net/database">Bunny Database</a> across increasing distances between the database and client locations. We then measured p95 latency for the same set of locations with read replication enabled, allowing reads to be served from regions local to the user, and compared the results.</p><h2 id="benchmark-setup">Benchmark setup</h2><p>Since Bunny Database is based on SQLite, we chose <a href="https://www.timestored.com/data/sample/sqlite">the Chinook dataset</a>, a commonly used sample SQLite database with a realistic transactional schema.</p><p>While the database engine itself is very fast, making query execution time negligible compared to network latency, we still opted for a realistic read query rather than a trivial <code>SELECT 1</code>, to better reflect how applications actually access data:</p>
<!--kg-card-begin: html-->
<div style="background-color: #1e1e1e; padding: 1em; border-radius: 6px; font-family: monospace; color: #ffffff; white-space: pre;">

<code style="color: #779fc9;">WITH</code> recent_invoices <code style="color: #779fc9;">AS</code> (
<code style="color: #779fc9;">SELECT</code>
InvoiceId,
InvoiceDate,
Total
<code style="color: #779fc9;">FROM</code> invoices
<code style="color: #779fc9;">WHERE</code> CustomerId <code style="color: #dab14b;">=</code> ?
<code style="color: #779fc9;">ORDER BY</code> InvoiceDate <code style="color: #779fc9;">DESC</code>
<code style="color: #779fc9;">LIMIT</code> ?
)

<code style="color: #779fc9;">SELECT</code>
ri.InvoiceId,
ri.InvoiceDate,
ri.Total,

ii.InvoiceLineId,
ii.Quantity,
ii.UnitPrice,

t.TrackId,
t.Name       <code style="color: #779fc9;">AS</code> TrackName,
a.Title      <code style="color: #779fc9;">AS</code> AlbumTitle,
ar.Name      <code style="color: #779fc9;">AS</code> ArtistName,
g.Name       <code style="color: #779fc9;">AS</code> GenreName

<code style="color: #779fc9;">FROM</code> recent_invoices ri
<code style="color: #779fc9;">JOIN</code> invoice_items ii <code style="color: #779fc9;">ON</code> ii.InvoiceId   <code style="color: #dab14b;">=</code> ri.InvoiceId
<code style="color: #779fc9;">JOIN</code> tracks t         <code style="color: #779fc9;">ON</code> t.TrackId     <code style="color: #dab14b;">=</code> ii.TrackId
<code style="color: #779fc9;">JOIN</code> albums a         <code style="color: #779fc9;">ON</code> a.AlbumId     <code style="color: #dab14b;">=</code> t.AlbumId
<code style="color: #779fc9;">JOIN</code> artists ar       <code style="color: #779fc9;">ON</code> ar.ArtistId   <code style="color: #dab14b;">=</code> a.ArtistId
<code style="color: #779fc9;">LEFT JOIN</code> genres g    <code style="color: #779fc9;">ON</code> g.GenreId     <code style="color: #dab14b;">=</code> t.GenreId

<code style="color: #779fc9;">ORDER BY</code>
ri.InvoiceDate    <code style="color: #779fc9;">DESC</code>,
ii.InvoiceLineId  <code style="color: #779fc9;">ASC</code>;
</div>

<!--kg-card-end: html-->
<p></p><p>Bunny Database is available in 41 regions worldwide, but to keep the benchmark focused, we limited the experiment to a smaller, representative set of locations:</p><ul><li><strong>Ashburn, Frankfurt, and Singapore as primary regions</strong>: these are commonly offered anchor regions across database providers and serve as practical reference points for North America, Europe, and Asia-Pacific.</li><li><strong>Palo Alto, London, Milan, S&#xE3;o Paulo, Tel Aviv, Tokyo, Cape Town, and Sydney as client locations and read replica regions</strong>: this set spans the globe and represents increasing geographic distance from Ashburn.</li></ul><p>As for technical implementation, we used <a href="https://k6.io/">Grafana k6</a> to conduct p95 latency measurements. Since the benchmark needed to run from the selected client locations, we ran k6 on <a href="http://docs.bunny.net/magic-containers">Magic Containers</a>, the bunny.net managed container service that shares the same regions with Bunny Database.</p><p>This setup allowed us to simulate the round-trip latency experienced by users in different parts of the world when accessing a centralized database.</p><h2 id="benchmark-results">Benchmark results</h2><p>The tables below show p95 read latency measured from each client location, first with a single-region database setup and then with <strong>read replication enabled</strong>, where queries are served from replicas deployed in the same regions as the clients, rather than always being routed to the primary region.</p><p>Client locations are ordered by increasing distance from Ashburn, and the same ordering is reused for Frankfurt and Singapore for consistency.</p><h3 id="read-latency-by-client-location">Read latency by client location</h3><p></p><p><strong>Primary region: Ashburn</strong></p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Primary-Region-Ashburn.png" class="kg-image" alt="How database location affects far-away users: benchmarking read latency in Bunny Database" loading="lazy" width="530" height="375"></figure><p><br><strong>Primary region: Frankfurt </strong></p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Primary-Region-Frankfurt.png" class="kg-image" alt="How database location affects far-away users: benchmarking read latency in Bunny Database" loading="lazy" width="530" height="375"></figure><p><strong>Primary region: Singapore </strong></p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Primary-Region-Singapore.png" class="kg-image" alt="How database location affects far-away users: benchmarking read latency in Bunny Database" loading="lazy" width="530" height="375"></figure><h3 id="notes-on-methodology">Notes on methodology</h3><p>Each data point represents the p95 latency observed within a single benchmark run consisting of hundreds of requests from a given client location. We didn&#x2019;t average results across multiple runs, which means transient network effects are preserved rather than smoothed out.</p><p>This reflects real-world conditions, where user requests experience the network as it is, not as an average.</p><p>Read replicas were deployed in the same regions as the client workloads, making this a best-case scenario for read locality. As a result, the &#x201C;with replication&#x201D; measurements represent the upper bound of what can be achieved when reads are served locally.</p><p>In practice, results will vary depending on region coverage, routing, and workload patterns &#x2014; but the underlying trend remains the same.</p><h3 id="what-these-results-mean-in-practice">What these results mean in practice</h3><ul><li><strong>Distance dominates latency in single-region setups</strong>: without replication, read latency increases sharply as client locations move farther away from the primary region, often reaching hundreds of milliseconds for intercontinental access.</li><li><strong>Read replication consistently collapses read latency across regions</strong>: with replication enabled, p95 read latency drops to single- or low double-digit milliseconds in nearly all locations, regardless of distance from the primary region.</li><li><strong>The biggest gains appear for far-away users</strong>: intercontinental client locations see latency reductions of up to 99%, turning multi-hundred-millisecond round trips into near-local reads.</li><li><strong>Replication doesn&#x2019;t eliminate all variability</strong>: some locations still show higher p95s than others, reflecting real-world network conditions and routing differences rather than database performance.</li><li><strong>The pattern holds across all primary regions tested</strong>: whether the primary region is Ashburn, Frankfurt, or Singapore, the overall trend remains the same: distance hurts without replication, and replication largely neutralizes it.</li></ul><h2 id="the-takeaway">The takeaway</h2><p>Our benchmark shows that database placement has a first-order impact on user experience for global applications. A single-region database may perform well for nearby users, but quickly becomes a bottleneck as traffic spreads geographically.</p><p>By serving reads from regions close to users, read replication shifts latency from being distance-bound to being dominated by local network conditions without requiring application-level caching or architectural workarounds.</p>]]></content:encoded></item><item><title><![CDATA[Announcing HopStart, cohort #1: meet the 3 startups building with bunny.net]]></title><description><![CDATA[We’ve been overwhelmed by the response to the program and received many strong applications. That said, narrowing it down to just three startups for this cohort wasn’t easy.]]></description><link>https://bunny.net/blog/announcing-hopstart-cohort-1-meet-the-3-startups-building-with-bunny-net/</link><guid isPermaLink="false">697c7388160dc403fbfced35</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Fri, 30 Jan 2026 09:58:36 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/01/Bunny-Hopstart.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/01/Bunny-Hopstart.png" alt="Announcing HopStart, cohort #1: meet the 3 startups building with bunny.net"><p>Last quarter we launched HopStart, our startup program to help founders worry less about infrastructure and focus more on their products, so they can build their dreams.</p><p>HopStart runs in quarterly cohorts. For each round, we select three winners and support them with up to <strong>$50,000 USD in credits</strong> for use on <a href="http://bunny.net">bunny.net</a>, alongside regular check-ins and feedback, as well as early access to upcoming features and products.</p><p>We&#x2019;ve been overwhelmed by the response to the program and received many strong applications. That said, narrowing it down to just three startups for this cohort wasn&#x2019;t easy. We looked at how each product could make a real difference for its users or industry, and at whether our platform could meaningfully support their growth.</p><p>Without further ado, meet <strong>HopStart cohort #1</strong>.</p><h2 id="1st-place-phare">1st place: Phare</h2><p><strong>Founder:</strong> Nicolas Beauvais</p><p><strong>Year founded:</strong> 2022</p><p><strong>URL:</strong> <a href="https://phare.io/">https://phare.io/</a></p><p><strong>Credits received:</strong> $50,000 for one year</p><p><strong>In the founder&#x2019;s words:</strong> &#x201C;Phare currently serves over 500 companies with uptime monitoring, incident management, and custom status pages, with a strong focus on EU-based providers, privacy, and user experience.&#x201D;</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-hopstart-1st-place-Phare-1.png" class="kg-image" alt="Announcing HopStart, cohort #1: meet the 3 startups building with bunny.net" loading="lazy" width="2000" height="1079" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-hopstart-1st-place-Phare-1.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-hopstart-1st-place-Phare-1.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/01/Bunny-hopstart-1st-place-Phare-1.png 1600w, https://bunny.net/blog/content/images/size/w2400/2026/01/Bunny-hopstart-1st-place-Phare-1.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="2nd-place-split-hero">2nd place: Split Hero</h2><p><strong>Founder:</strong> Adam Lacey</p><p><strong>Year founded:</strong> 2019</p><p><strong>URL:</strong> <a href="https://splithero.com/">https://splithero.com/</a></p><p><strong>Credits received:</strong> $25,000 for one year</p><p><strong>In the founder&#x2019;s words:</strong> &#x201C;We want to be the number one privacy-first, edge-native experimentation platform for agencies and product teams. It turns &#x2018;should we test?&#x2019; into a default &#x2018;yes&#x2019; by removing friction (no heavy scripts, no cookies, no PII), delivering variant decisions in under 30 ms, and keeping return visitors consistent via anonymous, deterministic IDs. A real-time event pipeline and warehouse-grade analytics (aggregated, de-identified) turn noise into decisions, not dashboards.&#x201D;</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-hopstart-2nd-place-Split-Hero.png" class="kg-image" alt="Announcing HopStart, cohort #1: meet the 3 startups building with bunny.net" loading="lazy" width="2000" height="1114" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-hopstart-2nd-place-Split-Hero.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-hopstart-2nd-place-Split-Hero.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/01/Bunny-hopstart-2nd-place-Split-Hero.png 1600w, https://bunny.net/blog/content/images/size/w2400/2026/01/Bunny-hopstart-2nd-place-Split-Hero.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="3rd-place-udocz">3rd place: uDocz</h2><p><strong>Founder:</strong> Carlos Effio</p><p><strong>Year founded:</strong> 2020</p><p><strong>URL:</strong> <a href="https://www.udocz.com/">https://www.udocz.com/</a></p><p><strong>Credits received:</strong> $10,000 for one year</p><p><strong>In the founder&#x2019;s words:</strong> &#x201C;uDocz is the AI operating system for higher education, improving student retention, staff productivity, and overall efficiency. Currently, we serve 4 million students every month. Our AI-powered learning platform helps universities provide better academic support to their students and faculty. We have managed to improve students&apos; course performance (a 30% boost in grades) and reduce dropout rates, enhance career readiness, and graduate profile. In addition, we have a direct-to-consumer model, where we currently serve millions of students every month with supplementary classroom help (class materials and AI tools).&#x201D;</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-hopstart-3rd-place-uDocz.png" class="kg-image" alt="Announcing HopStart, cohort #1: meet the 3 startups building with bunny.net" loading="lazy" width="2000" height="971" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-hopstart-3rd-place-uDocz.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-hopstart-3rd-place-uDocz.png 1000w, https://bunny.net/blog/content/images/size/w1600/2026/01/Bunny-hopstart-3rd-place-uDocz.png 1600w, https://bunny.net/blog/content/images/size/w2400/2026/01/Bunny-hopstart-3rd-place-uDocz.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="apply-for-the-next-hopstart-cohort">Apply for the next HopStart cohort</h2><p>Applications for cohort #2 are already open. To be considered for the next round, all you need to do is <a href="https://bunny.net/HopStart/#form">fill in this short form</a> before <strong>February 28</strong>.</p><p>We&#x2019;re looking for bold ideas with meaningful impact and a clear fit for <a href="http://bunny.net">bunny.net</a>&#x2019;s support. If you applied before but weren&#x2019;t selected, you&#x2019;re encouraged to apply again &#x2014; a different cohort can mean a better fit.</p><p>Three teams will be selected for the second cohort and announced no later than <strong>April 2026</strong>.</p><p>We&#x2019;re looking forward to seeing what you&#x2019;re building!</p>]]></content:encoded></item><item><title><![CDATA[Introducing the new bunny.net developer documentation]]></title><description><![CDATA[As our product offering grew, the docs became increasingly flat and unstructured. Everything lived at the same level, which made it hard for people to stay focused and find the right docs quickly.]]></description><link>https://bunny.net/blog/introducing-the-new-bunny-net-developer-documentation/</link><guid isPermaLink="false">697761a3160dc403fbfced08</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jamie Barton]]></dc:creator><pubDate>Tue, 27 Jan 2026 12:50:00 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2026/01/Bunny-reveal-2-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2026/01/Bunny-reveal-2-2.png" alt="Introducing the new bunny.net developer documentation"><p>Great developer experiences start with great documentation. That&apos;s why we just launched new bunny.net developer docs, rebuilt from the ground up to better support how developers actually build, explore, and integrate with bunny.net.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-4.png" class="kg-image" alt="Introducing the new bunny.net developer documentation" loading="lazy" width="1415" height="937" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-Edge-Scripting-Documentation-4.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-Edge-Scripting-Documentation-4.png 1000w, https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-4.png 1415w" sizes="(min-width: 720px) 720px"></figure><p>Our previous documentation lived on an outdated platform that didn&apos;t scale with <a href="http://bunny.net">bunny.net</a>&apos;s growth, nor with the expectations developers now have for modern dev tools.</p><p>As our product offering grew, the docs became increasingly flat and unstructured. Everything lived at the same level, which made it hard for people to stay focused and find the right docs quickly.</p><p>Listening to your feedback, one theme kept coming up: the experience felt overwhelming. Finding the right information often meant losing context, jumping between pages, or second-guessing whether you were even in the right place.</p><p>At the same time, the bar for developer documentation has risen. Devs expect modern, clean, and structured docs, AI-friendly content structures, copyable pages for LLMs and tools, and interactive API references with code snippets.</p><p><strong>So we decided it was time for a complete rethink.</strong></p><h2 id="the-solution-documentation-built-around-products">The solution: documentation built around products</h2><p>The biggest change is structure. Instead of a single, flat documentation space, each <a href="http://bunny.net/">bunny.net</a> product now has its own dedicated area. This allows developers to fully immerse themselves in one product at a time, without distractions.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-3-1.png" class="kg-image" alt="Introducing the new bunny.net developer documentation" loading="lazy" width="1149" height="859" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-Edge-Scripting-Documentation-3-1.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-Edge-Scripting-Documentation-3-1.png 1000w, https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-3-1.png 1149w" sizes="(min-width: 720px) 720px"></figure><p>The new documentation contains spaces for each product:</p><ul><li><strong>Magic Containers</strong>: Docker-based container deployment close to users</li><li><strong>Edge Scripting</strong>: Serverless functions with TypeScript</li><li><strong>Database</strong>: Serverless SQLite over HTTP</li><li><strong>CDN</strong>: Content delivery, acceleration, and edge rules</li><li><strong>Shield</strong>: WAF, DDoS protection, rate limiting, and bot detection</li><li><strong>Stream</strong>: Video streaming, encoding, and DRM</li><li><strong>Storage</strong>: Global object storage</li><li><strong>Optimizer</strong>: Automatic image and web optimization</li><li><strong>DNS</strong>: Ultra-fast, scriptable DNS with DNSSEC</li></ul><p>Within each product, you&apos;ll find clearly organized sections: <strong>Quickstarts</strong> to get up and running fast, <strong>Feature documentation</strong> that goes deep when you need it, <strong>Practical examples</strong> to see real-world usage, and <strong>Guides</strong> that stay relevant to the product you&apos;re working on.</p><p>The goal is simple: when you&apos;re working with a <a href="http://bunny.net">bunny.net</a> product, everything you need should be right there, in context.</p><h2 id="a-complete-api-overhaul">A complete API overhaul</h2><p>We&apos;ve significantly upgraded our API reference across all products. Our API documentation is now fully OpenAPI-spec-driven, which unlocks a much richer experience:</p><ul><li>Make real API requests directly from the browser</li><li>Explore accurate request and response examples</li><li>See up-to-date schemas generated from the source of truth</li><li>Understand endpoints faster with consistent, structured layouts</li></ul><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-2-1.png" class="kg-image" alt="Introducing the new bunny.net developer documentation" loading="lazy" width="1312" height="577" srcset="https://bunny.net/blog/content/images/size/w600/2026/01/Bunny-Edge-Scripting-Documentation-2-1.png 600w, https://bunny.net/blog/content/images/size/w1000/2026/01/Bunny-Edge-Scripting-Documentation-2-1.png 1000w, https://bunny.net/blog/content/images/2026/01/Bunny-Edge-Scripting-Documentation-2-1.png 1312w" sizes="(min-width: 720px) 720px"></figure><p>Whether you&apos;re exploring an API for the first time or debugging an integration, the new <a href="https://docs.bunny.net/api-reference">API Reference</a> is designed to be both powerful and approachable.</p><h2 id="sdks-examples-and-integrations">SDKs, examples, and integrations</h2><p>We&apos;ve added SDK documentation for Storage across multiple languages: <strong>TypeScript</strong>, <strong>PHP</strong>, <strong>.NET</strong>, and <strong>Java</strong>. Each SDK comes with installation instructions, authentication setup, and practical examples for common operations.</p><p>Documentation isn&apos;t just about explaining features. It&apos;s about showing how to use them. Each product includes practical, copy-paste-ready examples. Edge Scripting, for instance, comes with examples for common patterns like modifying response headers, handling redirects, and returning structured JSON and connecting to a database, all in TypeScript.</p><p>We&apos;ve also started to migrate some useful integration guides from our support knowledge base, covering popular platforms like WordPress, Shopware, Drupal, and Magento on the CMS side, plus cloud storage providers like Amazon S3, Azure Blob, Backblaze, and DigitalOcean Spaces.</p><h2 id="whats-next-hopping-forward">What&apos;s next: hopping forward</h2><p>This new documentation platform gives us the flexibility to keep improving: adding more guides, tutorials, embedding videos, embracing AI-assisted workflows, and evolving alongside the wider developer ecosystem.</p><p>The new documentation includes nearly <strong>300 new pages</strong> of content organized across quickstarts, feature guides, API references, SDK documentation, and real-world examples. And we&apos;re just getting started.</p><p>Most importantly, it gives us a solid foundation to continue listening to your feedback and iterating quickly.</p><h2 id="explore-the-new-docs">Explore the new docs</h2><p>We&apos;re excited to share this with the community and would love to hear what you think. Dive in, explore your favorite <a href="http://bunny.net">bunny.net</a> products, and let us know how the new experience works for you.</p><p><a href="https://docs.bunny.net/"><strong>Start exploring the new bunny.net developer documentation</strong></a></p>]]></content:encoded></item><item><title><![CDATA[You can now automatically generate video chapters in Bunny Stream]]></title><description><![CDATA[Smart chapters extend the Bunny Stream transcribing feature, which is powered by Whisper, the popular automatic speech recognition (ASR) model from OpenAI. Smart chapters use the video transcription to automatically generate chapter timestamps and titles.]]></description><link>https://bunny.net/blog/you-can-now-automatically-generate-video-chapters-in-bunny-stream/</link><guid isPermaLink="false">69258120160dc403fbfcecdc</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Marek Nalikowski]]></dc:creator><pubDate>Tue, 25 Nov 2025 10:32:13 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2025/11/Bunny-Stream-Smart-Chapters-1.webp" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2025/11/Bunny-Stream-Smart-Chapters-1.webp" alt="You can now automatically generate video chapters in Bunny Stream"><p>Video viewers love having chapters with clear titles they can hop between, even in shorter videos. But setting those up manually takes time and effort. You have to choose the right timestamps, name each section, and make sure it all makes sense.</p><p>We just fixed that for Bunny Stream users with <strong>smart chapters</strong>, a new feature that automatically generates chapters for your videos!</p><h2 id="how-smart-chapters-work">How smart chapters work</h2><p>Smart chapters extend the Bunny Stream transcribing feature, which is powered by Whisper, the popular automatic speech recognition (ASR) model from OpenAI. Smart chapters use the video transcription to automatically generate chapter timestamps and titles. Because of this, videos need to be transcribed or have a caption track already available before chapters can be created.</p><h2 id="how-to-enable-smart-chapters">How to enable smart chapters</h2><p>To automatically generate chapters for newly uploaded videos, choose a video library and go to <strong>Transcribing</strong> to toggle <strong>Smart chapters</strong> on.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2025/11/How-To-Enable-Smart-Chapters.png" class="kg-image" alt="You can now automatically generate video chapters in Bunny Stream" loading="lazy" width="2000" height="985" srcset="https://bunny.net/blog/content/images/size/w600/2025/11/How-To-Enable-Smart-Chapters.png 600w, https://bunny.net/blog/content/images/size/w1000/2025/11/How-To-Enable-Smart-Chapters.png 1000w, https://bunny.net/blog/content/images/size/w1600/2025/11/How-To-Enable-Smart-Chapters.png 1600w, https://bunny.net/blog/content/images/size/w2400/2025/11/How-To-Enable-Smart-Chapters.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Alternatively, you can enable smart chapters programmatically via the API:</p>
<!--kg-card-begin: html-->
<div style="background-color: #2d2d2d; padding: 1em; border-radius: 6px; font-family: monospace; color: #ffffff;">

  <code style="color: #DBDD91;">curl</code>
  <code style="color: #ffffff;"> --request POST \</code><br>

  &#xA0;&#xA0;&#xA0;&#xA0;<code style="color: #ffffff;">--url</code>
  <code style="color: #B2CD5B;"> https://api.bunny.net/videolibrary/*******</code> \<br>

  &#xA0;&#xA0;&#xA0;&#xA0;<code style="color: #ffffff;">--header</code>
  <code style="color: #B2CD5B;"> &apos;AccessKey: *********-*-420d-a6b4-ae2465b30fa5&apos;</code> \<br>

  &#xA0;&#xA0;&#xA0;&#xA0;<code style="color: #ffffff;">--header</code>
  <code style="color: #B2CD5B;"> &apos;accept: application/json&apos;</code> \<br>

  &#xA0;&#xA0;&#xA0;&#xA0;<code style="color: #ffffff;">--header</code>
  <code style="color: #B2CD5B;"> &apos;content-type: application/json&apos;</code> \<br>

  &#xA0;&#xA0;&#xA0;&#xA0;<code style="color: #ffffff;">--data</code>
  <code style="color: #B2CD5B;"> &apos;</code><br>

  <code style="color: #B2CD5B;">{</code><br>
  <code style="color: #B2CD5B;">&#xA0;&#xA0;&quot;EnableTranscribingChaptersGeneration&quot;: true</code><br>
  <code style="color: #B2CD5B;">}</code><br>

  <code style="color: #B2CD5B;">&apos;</code>

</div>

<!--kg-card-end: html-->
<p>Once enabled, all newly uploaded videos will have chapters automatically generated as part of the transcription process &#x2014; at no additional cost.</p><p>Additionally, instead of enabling smart chapters globally, you can generate them on a per-video basis via the <a href="https://docs.bunny.net/reference/video_smartgenerate">Trigger Smart actions endpoint</a>. This new endpoint also supports generating smart titles, descriptions, and moments.</p><p>For more info on programmatic access, refer to the <a href="https://docs.bunny.net/docs/stream-smart-chapters">smart chapters docs</a> and <a href="https://docs.bunny.net/reference/videolibrarypublic_update">API reference</a>.</p><h2 id="how-to-generate-chapters-for-previously-uploaded-videos">How to generate chapters for previously uploaded videos</h2><p>Once you&#x2019;ve enabled smart chapters and want to generate chapters for a previously uploaded video, open the video in the library and go to the <strong>Chapters</strong> tab, then click <strong>Generate Smart Chapters</strong>.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2025/11/How-To-Generate-Chapters.png" class="kg-image" alt="You can now automatically generate video chapters in Bunny Stream" loading="lazy" width="2000" height="1014" srcset="https://bunny.net/blog/content/images/size/w600/2025/11/How-To-Generate-Chapters.png 600w, https://bunny.net/blog/content/images/size/w1000/2025/11/How-To-Generate-Chapters.png 1000w, https://bunny.net/blog/content/images/size/w1600/2025/11/How-To-Generate-Chapters.png 1600w, https://bunny.net/blog/content/images/size/w2400/2025/11/How-To-Generate-Chapters.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Note: If the video you want to generate chapters for doesn&#x2019;t yet have a transcription, it will need to be transcribed first.</p><h2 id="how-to-edit-the-auto-generated-chapters">How to edit the auto-generated chapters</h2><p>If you&#x2019;re not fully satisfied with your video&#x2019;s automatically generated chapters, you can edit them under the <strong>Chapters</strong> tab. Adjust timestamps, change titles, or add new chapters as needed.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2025/11/How-to-Edit-chapters.png" class="kg-image" alt="You can now automatically generate video chapters in Bunny Stream" loading="lazy" width="2000" height="1014" srcset="https://bunny.net/blog/content/images/size/w600/2025/11/How-to-Edit-chapters.png 600w, https://bunny.net/blog/content/images/size/w1000/2025/11/How-to-Edit-chapters.png 1000w, https://bunny.net/blog/content/images/size/w1600/2025/11/How-to-Edit-chapters.png 1600w, https://bunny.net/blog/content/images/size/w2400/2025/11/How-to-Edit-chapters.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="how-to-track-transcription-costs">How to track transcription costs</h2><p>Generating chapters comes at no extra cost other than the cost of video transcription, which is charged at $0.10 per minute of video per language.</p><p>You can view your current usage in the dashboard under <strong>Balance</strong> &#x2192; <strong>Billing</strong> &#x2192; <strong>Stream add-ons</strong>.</p><p>You can also track your transcription stats, including cost, via the <a href="https://docs.bunny.net/reference/gettranscribingstatistics_statistics">Get Video Library Transcribing Statistics endpoint</a> we added recently.</p><h2 id="smart-chapters-now-available-in-bunny-stream">Smart chapters now available in Bunny Stream</h2><p>The new feature has now been rolled out to all Stream users!</p><p>No matter what kind of video content you&#x2019;re hosting, chapters add a bit of extra polish to your audience&#x2019;s experience. And now that you can generate them automatically when transcribing content, it&#x2019;s an absolute no-brainer.</p><p>Give smart chapters a try, and happy hosting!</p><h3 id="references">References</h3><ul><li><a href="https://docs.bunny.net/docs/stream-smart-chapters">Smart chapters docs</a></li><li><a href="http://docs.bunny.net/reference/videolibrarypublic_update">API reference</a></li></ul>]]></content:encoded></item><item><title><![CDATA[From preview to proven: Bunny Shield enters general availability]]></title><description><![CDATA[Since that day, Bunny Shield has quietly guarded tens of thousands of websites and APIs around the world. It’s blocked exploits before they reached production and absorbed floods that would have brought whole services down. Most of the time, no one even noticed, and that’s exactly how it should be.]]></description><link>https://bunny.net/blog/from-preview-to-proven-bunny-shield-enters-general-availability/</link><guid isPermaLink="false">6903612c160dc403fbfcec89</guid><category><![CDATA[News]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Joe Connolly]]></dc:creator><pubDate>Thu, 30 Oct 2025 13:07:34 GMT</pubDate><media:content url="https://bunny.net/blog/content/images/2025/10/Bunny-Shield-General-Availability.png" medium="image"/><content:encoded><![CDATA[<img src="https://bunny.net/blog/content/images/2025/10/Bunny-Shield-General-Availability.png" alt="From preview to proven: Bunny Shield enters general availability"><p>Six months ago, we launched Bunny Shield in Preview with one goal: to make serious, scalable security something anyone could use. No enterprise contracts. No hidden pricing. Just strong protection that was easy to turn on.</p><p>It was the next step in a bigger journey, evolving <a href="http://bunny.net/">bunny.net</a> from a content delivery network into a global platform that can power and protect your entire website or application. Performance and security, working hand in hand at the edge.</p><p>Since that day, Bunny Shield has quietly guarded tens of thousands of websites and APIs around the world. It&#x2019;s blocked exploits before they reached production and absorbed floods that would have brought whole services down. Most of the time, no one even noticed, and that&#x2019;s exactly how it should be.</p><p>Now, Bunny Shield is ready to move forward. Today, we&#x2019;re graduating from Preview and officially moving into General Availability. After trillions of requests, months of testing, and continuous tuning, Bunny Shield has matured into a complete, reliable layer of defense built directly into the <a href="http://bunny.net">bunny.net</a> edge.</p><h2 id="the-journey-from-preview-to-proven">The journey from preview to proven</h2><p>When Bunny Shield launched, it started as an idea: security that worked as smoothly as delivery. What followed was six months of learning in production. We saw everything from quiet credential stuffing to coordinated botnets and sudden floods. Each event helped us refine how Shield reacts, adapts, and protects without slowing you down.</p><p>It wasn&#x2019;t about adding features for the sake of it, but about shaping something that felt effortless to use and powerful when it mattered. Today, Bunny Shield stands as a proven part of the stack. Fast, consistent, and built for real traffic.</p><h2 id="built-for-the-real-world">Built for the real world</h2><p>Bunny Shield now protects against the full range of threats across the network. DDoS mitigation absorbs attacks at the edge so they never reach your origin. The <a href="https://bunny.net/blog/bunny-shield-waf-fast-flexible-and-regex-ready/">WAF</a> inspects every request with flexible, customizable rules while staying focused on accuracy. <a href="https://bunny.net/blog/control-the-chaos-why-legacy-rate-limiting-isnt-enough-anymore/">Rate limiting</a> helps you keep abusive traffic in check globally. <a href="https://bunny.net/blog/introducing-bunny-shield-bot-detection-smart-silent-and-built-for-the-modern-web/">Bot detection</a> identifies and stops automated traffic that tries to blend in. <a href="https://bunny.net/blog/introducing-bunny-shield-access-lists-let-the-good-traffic-hop-through/">Access lists</a> give full control over who gets through. <a href="https://bunny.net/blog/introducing-bunny-shield-upload-scanning-the-end-of-risky-uploads/">Upload scanning</a> keeps malicious files out before they cause harm.</p><p>And coming soon, API Guardian will extend that same protection to APIs and AI-driven applications, adding deeper inspection and schema-aware validation. All of it runs natively at the edge, quietly, with almost no latency.</p><h2 id="transparent-by-design">Transparent by design</h2><p>Transparency has always been at the core of Bunny Shield. Every plan is built to be clear and predictable, with straightforward usage limits and simple overage pricing that scales with you. Whether you&#x2019;re running a personal project or a global platform, you know exactly what you&#x2019;re getting and what it costs.</p><p>Only clean requests count toward your plan, and our clean-request model makes it easy to understand how traffic is counted, with only legitimate requests billed. Anything blocked by Shield, whether through DDoS mitigation, the WAF, access lists, bot detection, or upload scanning, is excluded from your usage.</p><p>The result is a plan structure that grows fairly with your traffic without hidden costs or surprises.</p><h2 id="what-the-numbers-show">What the numbers show</h2><p>Six months on, Bunny Shield has quietly processed <strong>over 51.9 trillion requests</strong>, filtering out <strong>16.4 billion malicious ones</strong> before they could cause harm. Across tens of thousands of websites, it&#x2019;s kept performance steady and traffic secure without missing a beat.</p><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2025/10/Bunny-Shield-mitigation.png" class="kg-image" alt="From preview to proven: Bunny Shield enters general availability" loading="lazy" width="2000" height="693" srcset="https://bunny.net/blog/content/images/size/w600/2025/10/Bunny-Shield-mitigation.png 600w, https://bunny.net/blog/content/images/size/w1000/2025/10/Bunny-Shield-mitigation.png 1000w, https://bunny.net/blog/content/images/size/w1600/2025/10/Bunny-Shield-mitigation.png 1600w, https://bunny.net/blog/content/images/size/w2400/2025/10/Bunny-Shield-mitigation.png 2400w" sizes="(min-width: 720px) 720px"></figure><p><em>Bunny Shield mitigating a large-scale attack. Challenging more than 142 million abusive requests and blocking nearly 100 million in a single day with zero impact to performance.</em></p><p>Across the edge, Bunny Shield&#x2019;s layered defenses work in concert. Here&#x2019;s what that looks like in numbers:</p><ul><li>DDoS Attacks<ul><li>3.6 billion challenged before reaching origins</li><li>42.9 million threat sources blocked</li><li>2.1 billion logged for anomaly detection</li></ul></li><li>Bot Detection<ul><li>986 million challenges issued</li><li>1.16 billion logged for behavioral analysis</li></ul></li><li>Rule Engine<ul><li>177 million blocked by custom or managed WAF rules</li><li>2.2 billion logged for tuning and insights</li></ul></li><li>Access Lists<ul><li>188 million challenged</li><li>133 million blocked by access rules</li><li>360 million logged for visibility</li></ul></li><li>Rate Limiting<ul><li>3.6 billion challenged globally</li><li>1.7 billion blocked for exceeding thresholds</li><li>37 million logged for visibility and refinement</li></ul></li><li>Upload Scanning<ul><li>1.55 million files scanned for malware and unsafe content</li></ul></li></ul><figure class="kg-card kg-image-card"><img src="https://bunny.net/blog/content/images/2025/10/Malicious-requests.png" class="kg-image" alt="From preview to proven: Bunny Shield enters general availability" loading="lazy" width="1496" height="706" srcset="https://bunny.net/blog/content/images/size/w600/2025/10/Malicious-requests.png 600w, https://bunny.net/blog/content/images/size/w1000/2025/10/Malicious-requests.png 1000w, https://bunny.net/blog/content/images/2025/10/Malicious-requests.png 1496w" sizes="(min-width: 720px) 720px"></figure><p><em>Monthly malicious request volume, April&#x2013;October. A massive DDoS surge in September pushed malicious traffic to 7.9 billion. A vivid example of how unpredictable the threat landscape can be before settling back to normal levels in October.</em></p><p>Behind each of those numbers is a request that stayed online, a user who never saw an error page, and a builder who could keep focusing on what mattered instead of firefighting.</p><h2 id="the-road-ahead">The road ahead</h2><p>Security never stands still and neither does Bunny Shield. The next stage is all about depth. Smarter detection, better visibility, and even greater trust.</p><p><strong>API Guardian</strong> leads the way, adding schema-aware inspection, stronger authorization validation, and early AI protections for APIs and intelligent applications. It understands context, not just traffic, helping stop abuse before it reaches your logic layer.</p><p><strong>Upload scanning</strong> continues to evolve, catching malware, spam, and harmful content continuously before it ever reaches your origin.</p><p><strong>Bot detection</strong> is growing more intelligent through new behavioral models and anomaly analysis that identify subtle automation patterns while reducing friction for legitimate users.</p><p>As the landscape shifts, automated scraping and AI-powered bots are driving up bandwidth costs and putting more pressure on infrastructure. Bunny Shield is evolving to give you greater control over these threats, helping you defend your content, manage costs, and maintain performance without constant intervention.</p><p>Observability is expanding across the platform. Soon, Bunny Shield will provide clearer insights into live attacks and overall traffic behavior, making it easier to understand what&#x2019;s happening at the edge in real time. Richer analytics and deeper visibility will turn defense into something measurable and transparent.</p><p>DDoS alerts are also coming soon, giving you instant visibility when large-scale attacks are detected and mitigated. You&#x2019;ll be able to see when and how Bunny Shield steps in to protect your traffic, with real-time updates and clear summaries that bring peace of mind without adding noise.</p><p>Together, these improvements are shaping Bunny Shield into more than a security product. It&#x2019;s becoming the intelligence layer of the <a href="http://bunny.net">bunny.net</a> platform. A foundation that powers, protects, and accelerates everything built on it.</p><h2 id="ready-for-whatever-comes-next">Ready for whatever comes next</h2><p>As Bunny Shield moves into General Availability, it carries six months of lessons, testing, and real-world proof. It&#x2019;s faster, smarter, and easier to use than ever, built for anyone, from first projects to global platforms.</p><p>Thank you to everyone who helped shape it during Preview. Your feedback, ideas, and patience made this release what it is.</p><p>Bunny Shield is now generally available.</p><p>Turn it on. Let it run. Stay protected.</p><p>This is just the beginning of what <a href="http://bunny.net">bunny.net</a> is becoming. A platform built to power and protect the internet, one hop at a time.</p><p>Explore the <strong>Advanced</strong>, <strong>Business</strong>, and <strong>Enterprise</strong> plans to find your perfect level of protection and hop into stronger security today.</p>]]></content:encoded></item></channel></rss>