CINXE.COM
Open Source
<!doctype html> <html lang="en-us" dir="ltr"> <head> <base href="https://blog.cloudflare.com/tag/open-source/"> <script async src="https://ot.www.cloudflare.com/public/vendor/onetrust/scripttemplates/otSDKStub.js" data-document-language="true" type="text/javascript" data-domain-script="b1e05d49-f072-4bae-9116-bdb78af15448"></script> <meta name="HandheldFriendly" content="True"> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="baidu-site-verification" content="KeThzeyMOr"> <meta name="baidu-site-verification" content="code-NIlrS7gNhx"> <meta charset="UTF-8"> <meta name="description" content="Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet."> <title>Open Source</title> <meta name="title" content="Open Source"> <meta name="msvalidate.01" content="CF295E1604697F9CAD18B5A232E871F6"> <meta class="swiftype" name="language" data-type="string" content="en"> <script src="/static/z/i.js" type="text/javascript" referrerpolicy="origin"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-32x32.png"> <link rel="mask-icon" href="/images/favicon-32x32.png" color="#f78100"> <link rel="stylesheet" href="/themes/ashes.min.css"> <link rel="sitemap" href="/sitemap.xml"> <meta name="msapplication-TileColor" content="#da532c"> <meta name="theme-color" content="#ffffff"> <link rel="canonical" href="https://blog.cloudflare.com/tag/open-source"> <link rel="alternate" type="application/rss+xml" title="Cloudflare Open Source RSS Feed" href="/tag/open-source//rss"> <link rel="alternate" hreflang="en-us" href="https://blog.cloudflare.com/tag/open-source/"> <link rel="alternate" hreflang="de-de" href="https://blog.cloudflare.com/de-de/tag/open-source/"> <link rel="alternate" hreflang="es-es" href="https://blog.cloudflare.com/es-es/tag/open-source/"> <link rel="alternate" hreflang="fr-fr" href="https://blog.cloudflare.com/fr-fr/tag/open-source/"> <link rel="alternate" hreflang="it-it" href="https://blog.cloudflare.com/it-it/tag/open-source/"> <link rel="alternate" hreflang="ja-jp" href="https://blog.cloudflare.com/ja-jp/tag/open-source/"> <link rel="alternate" hreflang="ko-kr" href="https://blog.cloudflare.com/ko-kr/tag/open-source/"> <link rel="alternate" hreflang="zh-tw" href="https://blog.cloudflare.com/zh-tw/tag/open-source/"> <link rel="alternate" hreflang="zh-cn" href="https://blog.cloudflare.com/zh-cn/tag/open-source/"> <link rel="alternate" hreflang="pt-br" href="https://blog.cloudflare.com/pt-br/tag/open-source/"> <link rel="alternate" hreflang="ru-ru" href="https://blog.cloudflare.com/ru-ru/tag/open-source/"> <link rel="alternate" hreflang="id-id" href="https://blog.cloudflare.com/id-id/tag/open-source/"> <link rel="alternate" hreflang="th-th" href="https://blog.cloudflare.com/th-th/tag/open-source/"> <link rel="alternate" hreflang="vi-vn" href="https://blog.cloudflare.com/vi-vn/tag/open-source/"> <link rel="alternate" hreflang="pl-pl" href="https://blog.cloudflare.com/pl-pl/tag/open-source/"> <link rel="alternate" hreflang="et-ee" href="https://blog.cloudflare.com/et-ee/tag/open-source/"> <link rel="alternate" hreflang="lv-lv" href="https://blog.cloudflare.com/lv-lv/tag/open-source/"> <link rel="alternate" hreflang="lt-lt" href="https://blog.cloudflare.com/lt-lt/tag/open-source/"> <link rel="alternate" hreflang="ar-ar" href="https://blog.cloudflare.com/ar-ar/tag/open-source/"> <link rel="alternate" hreflang="he-il" href="https://blog.cloudflare.com/he-il/tag/open-source/"> <link rel="alternate" hreflang="sv-se" href="https://blog.cloudflare.com/sv-se/tag/open-source/"> <link rel="alternate" hreflang="nl-nl" href="https://blog.cloudflare.com/nl-nl/tag/open-source/"> <link rel="alternate" hreflang="tr-tr" href="https://blog.cloudflare.com/tr-tr/tag/open-source/"><!-- General Meta Tags --> <meta property="article:publisher" content="https://www.facebook.com/cloudflare"><!-- Facebook Meta Tags --> <meta property="og:site_name" content="The Cloudflare Blog"> <meta property="og:type" content="website"> <meta property="og:title" content="The Cloudflare Blog: Open Source"> <meta property="og:description" content="Collection of Cloudflare blog posts tagged 'Open Source'"> <meta property="og:url" content="https://blog.cloudflare.com/tag/open-source"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="628"><!-- Twitter/X Meta Tags --> <meta name="twitter:title" content="The Cloudflare Blog: Open Source"> <meta name="twitter:description" content="Collection of Cloudflare blog posts tagged 'Open Source'"> <meta name="twitter:url" content="https://blog.cloudflare.com/tag/open-source"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:site" content="@cloudflare"> <meta property="og:image"> <meta name="twitter:image"> <link rel="stylesheet" href="/_astro/index.5BtHvQ-S.css"> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style> <script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();;(()=>{var b=Object.defineProperty;var f=(c,o,i)=>o in c?b(c,o,{enumerable:!0,configurable:!0,writable:!0,value:i}):c[o]=i;var l=(c,o,i)=>(f(c,typeof o!="symbol"?o+"":o,i),i);var p;{let c={0:t=>m(t),1:t=>i(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(i(t)),5:t=>new Set(i(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t)},o=t=>{let[e,r]=t;return e in c?c[e](r):void 0},i=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([e,r])=>[e,o(r)]));customElements.get("astro-island")||customElements.define("astro-island",(p=class extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var d;if(!this.hydrator||!this.isConnected)return;let e=(d=this.parentElement)==null?void 0:d.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let r=this.querySelectorAll("astro-slot"),a={},h=this.querySelectorAll("template[data-astro-template]");for(let n of h){let s=n.closest(this.tagName);s!=null&&s.isSameNode(this)&&(a[n.getAttribute("data-astro-template")||"default"]=n.innerHTML,n.remove())}for(let n of r){let s=n.closest(this.tagName);s!=null&&s.isSameNode(this)&&(a[n.getAttribute("name")||"default"]=n.innerHTML)}let u;try{u=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(n){let s=this.getAttribute("component-url")||"<unknown>",y=this.getAttribute("component-export");throw y&&(s+=` (export ${y})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),n),n}await this.hydrator(this)(this.Component,u,a,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),r.disconnect(),this.childrenConnectedCallback()},r=new MutationObserver(()=>{var a;((a=this.lastChild)==null?void 0:a.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});r.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}start(){let e=JSON.parse(this.getAttribute("opts")),r=this.getAttribute("client");if(Astro[r]===void 0){window.addEventListener(`astro:${r}`,()=>this.start(),{once:!0});return}Astro[r](async()=>{let a=this.getAttribute("renderer-url"),[h,{default:u}]=await Promise.all([import(this.getAttribute("component-url")),a?import(a):()=>()=>{}]),d=this.getAttribute("component-export")||"default";if(!d.includes("."))this.Component=h[d];else{this.Component=h;for(let n of d.split("."))this.Component=this.Component[n]}return this.hydrator=u,this.hydrate},e,this)}attributeChangedCallback(){this.hydrate()}},l(p,"observedAttributes",["props"]),p))}})();</script> <meta http-equiv="X-Translated-By" content="Google"> <meta http-equiv="X-Translated-To" content="de"> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=corsproxy" data-sourceurl="https://blog.cloudflare.com/tag/open-source/"></script> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet"> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/exm=corsproxy/ed=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=phishing_protection" data-phishing-protection-enabled="false" data-forms-warning-enabled="true" data-source-url="https://blog.cloudflare.com/tag/open-source/"></script> <meta name="robots" content="none"> </head> <body> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/exm=corsproxy,phishing_protection/ed=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=navigationui" data-environment="prod" data-proxy-url="https://blog-cloudflare-com.translate.goog" data-proxy-full-url="https://blog-cloudflare-com.translate.goog/tag/open-source/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" data-source-url="https://blog.cloudflare.com/tag/open-source/" data-source-language="pl" data-target-language="de" data-display-language="en-GB" data-detected-source-language="" data-is-source-untranslated="false" data-source-untranslated-url="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://blog.cloudflare.com/tag/open-source/&anno=2" data-client="tr"></script><astro-island uid="Z2tAFgn" component-url="/_astro/GoogleAnalytics.DKWYs_Ts.js" component-export="GoogleAnalytics" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"title":[0,"Open Source"],"canonical":[0,"https://blog.cloudflare.com/tag/open-source"],"info":[0],"tagInfo":[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"slug":[0,"open-source"],"url":[0,"https://blog.cloudflare.com/open-source"],"name":[0,"Open Source"],"visibility":[0,"public"],"feature_image":[0,""]}],"authorInfo":[0],"translatedPosts":[1,[]]}" ssr="" client="only" opts="{"name":"GoogleAnalytics","value":"react"}"></astro-island> <script>(()=>{var i=t=>{let e=async()=>{await(await t())()};"requestIdleCallback"in window?window.requestIdleCallback(e):setTimeout(e,200)};(self.Astro||(self.Astro={})).idle=i;window.dispatchEvent(new Event("astro:idle"));})();</script><astro-island uid="ZksVVU" prefix="r8" component-url="/_astro/Navigation.Ha-IvqmS.js" component-export="Navigation" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"title":[0,"The Cloudflare Blog"],"logo":[0,"//images.ctfassets.net/zkvhlag99gkb/69RwBidpiEHCDZ9rFVVk7T/092507edbed698420b89658e5a6d5105/CF_logo_stacked_blktype.png"],"pagesStore":[0,{"page":[0,"Tag"],"slug":[0,"open-source"],"navData":[1,[[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"6QktrXeEFcl4e2dZUTZVGl"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:43:20.198Z"],"updatedAt":[0,"2024-10-10T07:31:56.525Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,5],"revision":[0,3],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Product News"],"name":[0,"Product News"],"slug":[0,"product-news"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"48r7QV00gLMWOIcM1CSDRy"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:54:22.790Z"],"updatedAt":[0,"2024-10-10T07:30:18.450Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,9],"revision":[0,5],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Speed & Reliability"],"name":[0,"Speed & Reliability"],"slug":[0,"speed-and-reliability"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"6Mp7ouACN2rT3YjL1xaXJx"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:42:46.231Z"],"updatedAt":[0,"2024-10-10T07:27:43.433Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,7],"revision":[0,4],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Security"],"name":[0,"Security"],"slug":[0,"security"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"J61Eszqn98amrYHq4IhTx"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:43:46.068Z"],"updatedAt":[0,"2024-10-10T07:24:27.531Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,11],"revision":[0,6],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Zero Trust"],"name":[0,"Zero Trust"],"slug":[0,"zero-trust"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:43:21.536Z"],"updatedAt":[0,"2024-10-10T07:19:10.215Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,9],"revision":[0,5],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Developers"],"name":[0,"Developers"],"slug":[0,"developers"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"6Foe3R8of95cWVnQwe5Toi"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T22:44:28.803Z"],"updatedAt":[0,"2024-10-10T07:14:33.876Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,13],"revision":[0,7],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"AI"],"name":[0,"AI"],"slug":[0,"ai"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"7ibnRrkJ2kfMuo3JOo0Y69"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:47:23.765Z"],"updatedAt":[0,"2024-10-10T07:12:33.734Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,5],"revision":[0,3],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Policy"],"name":[0,"Policy"],"slug":[0,"policy"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"V86khSc459Yi1AhTlvtY7"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:46:53.657Z"],"updatedAt":[0,"2024-10-10T07:08:03.099Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,10],"revision":[0,5],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Partners"],"name":[0,"Partners"],"slug":[0,"partners"],"featured":[0,true]}]}],[0,{"metadata":[0,{"tags":[1,[]],"concepts":[1,[]]}],"sys":[0,{"space":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"Space"],"id":[0,"zkvhlag99gkb"]}]}],"id":[0,"4g8tPriKOAUwdUT4jNPebe"],"type":[0,"Entry"],"createdAt":[0,"2024-10-09T19:46:40.927Z"],"updatedAt":[0,"2024-10-10T07:05:21.343Z"],"environment":[0,{"sys":[0,{"id":[0,"master"],"type":[0,"Link"],"linkType":[0,"Environment"]}]}],"publishedVersion":[0,9],"revision":[0,5],"contentType":[0,{"sys":[0,{"type":[0,"Link"],"linkType":[0,"ContentType"],"id":[0,"blogTag"]}]}],"locale":[0,"en-US"]}],"fields":[0,{"entryTitle":[0,"Life at Cloudflare"],"name":[0,"Life at Cloudflare"],"slug":[0,"life-at-cloudflare"],"featured":[0,true]}]}]]],"translationsAvailable":[0]}],"locale":[0,"en-us"],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="idle" opts="{"name":"NavigationComponent","value":true}" await-children=""> <header class="flex flex-row flex-wrap justify-between items-flex-end mw8 center mv3 pl3 pr1"> <div class="w-100 flex items-flex-end justify-between justify-start-l"> <div class="w-100 tr flex justify-end"> <div class="flex justify-between items-center"> <span class="dn di-l pr1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://dash.cloudflare.com/sign-up" class="f1 blue1 dn di-l b no-underline underline-hover" target="_blank" rel="noreferrer">Get Started Free</a></span><span class="f1 gray4 dn di-l pr1">|</span><span class="dn di-l"><a target="_blank" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/plans/enterprise/contact/" class="f1 gray4 no-underline underline-hover pr1" rel="noreferrer">Contact Sales</a></span><span class="f1 gray4 dn di-l pr1">|</span> <div class="relative flex cf-dropdown"> <div class="flex items-center" dir="ltr"> <button type="button" class="f1 gray4 no-underline language-picker js-language-picker" style="background:transparent;border:none;padding:0"><span class="language-picker__globe-icon"></span><span class="language-picker__caret-icon ph1">▼</span></button> </div> </div> </div> </div> </div> <div class="w-100 w-50-l flex items-end nb5 nb1-l"> <a href="https://blog-cloudflare-com.translate.goog/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="header-logo mr4 dn db-l"><img class="header-logo" src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/69RwBidpiEHCDZ9rFVVk7T/092507edbed698420b89658e5a6d5105/CF_logo_stacked_blktype.png" alt="The Cloudflare Blog" width="170" height="57"></a> <h2 class="mt0 mb1 dn di-l"><a href="https://blog-cloudflare-com.translate.goog/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f5 gray3 no-underline"><span class="dn di-l">The Cloudflare Blog</span></a></h2> </div> <div class="w-100 w-50-l dn db-l"> <div class="w-100 tr mkto-sub-message"> <p class="f2">Subscribe to receive notifications of new posts:</p> </div> <div class="w-100 tr"> <div class="marketo-form-container"> <form id="mktoForm_1653"> <div class="top-subscribe-form-container"> <div class="top-subscribe-form-field"> <input placeholder="Email Address" class="top-subscribe-form-input" name="email" type="email" title="Must be valid email."> </div><button class="top-subscribe-form-button" type="button">Subscribe</button> </div> </form> </div> </div> </div> </header> <nav dir="ltr" class="bb b--black-10 db dn-l w-100 ph3 "> <div class=" flex justify-between items-center" style="height:44px"> <a href="https://blog-cloudflare-com.translate.goog/search?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB"><img class="h-6 w-6" src="/images/magnifier.svg" alt="magnifier icon"></a><button type="button" style="background:transparent;border:none"><img src="/images/hamburger.svg" alt="hamburger menu"></button> </div> <div class="js-mobile-nav-container dn"> <div class="flex flex-column flex-wrap bg-gray9 o-95 absolute w-90 ph3 z-1"> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/product-news?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Product News</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/speed-and-reliability?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Speed & Reliability</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/security?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Security</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/zero-trust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Zero Trust</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Developers</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">AI</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/policy?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Policy</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/partners?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Partners</a> </div> <div class="pv3 ph2 tl"> <a href="https://blog-cloudflare-com.translate.goog/tag/life-at-cloudflare?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw7">Life at Cloudflare</a> </div> </div> </div> </nav> <nav id="nav" class="w-100 bb-0 bb-l b--black-10 z-1"> <div id="desktop-nav-items-container" class="flex flex-wrap justify-between items-center mw8 center mv3 mv0-l"> <div data-tag="product-news" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/product-news?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Product News</a> </div> <div data-tag="speed-and-reliability" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/speed-and-reliability?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Speed & Reliability</a> </div> <div data-tag="security" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/security?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Security</a> </div> <div data-tag="zero-trust" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/zero-trust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Zero Trust</a> </div> <div data-tag="developers" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Developers</a> </div> <div data-tag="ai" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">AI</a> </div> <div data-tag="policy" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/policy?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Policy</a> </div> <div data-tag="partners" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/partners?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Partners</a> </div> <div data-tag="life-at-cloudflare" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"> <a href="https://blog-cloudflare-com.translate.goog/tag/life-at-cloudflare?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f2 fw5 pv3">Life at Cloudflare</a> </div> <div class="nav-item ml2 mr3 dn db-l pv3" data-tag="search icon"> <a href="https://blog-cloudflare-com.translate.goog/search/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB"><img id="search-icon" class="h-6 w-6" src="/images/magnifier.svg" alt="magnifier icon"></a> </div> </div> </nav><!--astro:end--> </astro-island> <script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event("astro:load"));})();</script> <div class="flex flex-row flex-wrap mw8 center bb b--gray8 ph3"> <h1 class="site-title f7 fw4 mt4 mb3 mv4-l">Open Source</h1> </div> <main id="site-main" class="flex flex-row flex-wrap mw8 center pt0 pt3-l mt4-l"><astro-island uid="1WeKB3" prefix="r0" component-url="/_astro/PostCard.DWAKGH4e.js" component-export="PostCard" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"currentPage":[0,1],"isFeaturedImageFirstPost":[0,true],"post":[0,{"id":[0,"2hySj1JFTXmlofjA6IRijm"],"title":[0,"Is this thing on? Using OpenBMC and ACPI power states for reliable server boot"],"slug":[0,"how-we-use-openbmc-and-acpi-power-states-to-monitor-the-state-of-our-servers"],"excerpt":[0,"Cloudflare’s global fleet benefits from being managed by open source firmware for the Baseboard Management Controller (BMC), OpenBMC. This has come with various challenges, some of which we discuss here with an explanation of how the open source nature of the firmware for the BMC enabled us to fix the issues and maintain a more stable fleet."],"featured":[0,false],"html":[0,"\n <div class=\"flex anchor relative\">\n <h2 id=\"introduction\">Introduction</h2>\n <a href=\"#introduction\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>At Cloudflare, we provide a range of services through our global network of servers, located in <a href=\"https://www.cloudflare.com/network/\"><u>330 cities</u></a> worldwide. When you interact with our long-standing <a href=\"https://www.cloudflare.com/application-services/products/\"><u>application services</u></a>, or newer services like <a href=\"https://ai.cloudflare.com/?_gl=1*1vedsr*_gcl_au*NzE0Njc1NTIwLjE3MTkzMzEyODc.*_ga*NTgyMWU1Y2MtYTI2NS00MDA3LTlhZDktYWUxN2U5MDkzYjY3*_ga_SQCRB0TXZW*MTcyMTIzMzM5NC4xNS4xLjE3MjEyMzM1MTguMC4wLjA.\"><u>Workers AI</u></a>, you’re in contact with one of our fleet of thousands of servers which support those services.</p><p>These servers which provide Cloudflare services are managed by a Baseboard Management Controller (BMC). The BMC is a special purpose processor — different from the Central Processing Unit (CPU) of a server — whose sole purpose is ensuring a smooth operation of the server.</p><p>Regardless of the server vendor, each server has this BMC. The BMC runs independently of the CPU and has its own embedded operating system, usually referred to as <a href=\"https://en.wikipedia.org/wiki/Firmware\"><u>firmware</u></a>. At Cloudflare, we customize and deploy a server-specific version of the BMC firmware. The BMC firmware we deploy at Cloudflare is based on the <a href=\"https://www.openbmc.org/\"><u>Linux Foundation Project for BMCs, OpenBMC</u></a>. OpenBMC is an open-sourced firmware stack designed to work across a variety of systems including enterprise, telco, and cloud-scale data centers. The open-source nature of OpenBMC gives us greater flexibility and ownership of this critical server subsystem, instead of the closed nature of proprietary firmware. This gives us transparency (which is important to us as a security company) and allows us faster time to develop custom features/fixes for the BMC firmware that we run on our entire fleet.</p><p>In this blog post, we are going to describe how we customized and extended the OpenBMC firmware to better monitor our servers’ boot-up processes to start more reliably and allow better diagnostics in the event that an issue happens during server boot-up.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"server-subsystems\">Server subsystems</h2>\n <a href=\"#server-subsystems\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Server systems consist of multiple complex subsystems that include the processors, memory, storage, networking, power supply, cooling, etc. When booting up the host of a server system, the power state of each subsystem of the server is changed in an asynchronous manner. This is done so that subsystems can initialize simultaneously, thereby improving the efficiency of the boot process. Though started asynchronously, these subsystems may interact with each other at different points of the boot sequence and rely on handshake/synchronization to exchange information. For example, during boot-up, the <a href=\"https://en.wikipedia.org/wiki/UEFI\"><u>UEFI (Universal Extensible Firmware Interface)</u></a>, often referred to as the <a href=\"https://en.wikipedia.org/wiki/BIOS\"><u>BIOS</u></a>, configures the motherboard in a phase known as the Platform Initialization (PI) phase, during which the UEFI collects information from subsystems such as the CPUs, memory, etc. to initialize the motherboard with the right settings.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6csPNEksLXsGgt3dq5xZ0S/3236656dbc01f3085bada5af853c3516/image1.png\" alt=\"\" class=\"kg-image\" width=\"1999\" height=\"978\" loading=\"lazy\"/>\n </figure><p><sup><i>Figure 1: Server Boot Process</i></sup></p><p>When the power state of the subsystems, handshakes, and synchronization are not properly managed, there may be race conditions that would result in failures during the boot process of the host. Cloudflare experienced some of these boot-related failures while rolling out open source firmware (<a href=\"https://en.wikipedia.org/wiki/OpenBMC\"><u>OpenBMC</u></a>) to the Baseboard Management Controllers (BMCs) of our servers. </p>\n <div class=\"flex anchor relative\">\n <h2 id=\"baseboard-management-controller-bmc-as-a-manager-of-the-host\">Baseboard Management Controller (BMC) as a manager of the host</h2>\n <a href=\"#baseboard-management-controller-bmc-as-a-manager-of-the-host\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>A BMC is a specialized microprocessor that is attached to the board of a host (server) to assist with remote management capabilities of the host. Servers usually sit in data centers and are often far away from the administrators, and this creates a challenge to maintain them at scale. This is where a BMC comes in, as the BMC serves as the interface that gives administrators the ability to securely and remotely access the servers and carry out management functions. The BMC does this by exposing various interfaces, including <a href=\"https://en.wikipedia.org/wiki/Intelligent_Platform_Management_Interface\"><u>Intelligent Platform Management Interface (IPMI)</u></a> and <a href=\"https://www.dmtf.org/standards/redfish\"><u>Redfish</u></a>, for distributed management. In addition, the BMC receives data from various sensors/devices (e.g. temperature, power supply) connected to the server, and also the operating parameters of the server, such as the operating system state, and publishes the values on its IPMI and Redfish interfaces.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33dNmfyjqrbAGvcbZLTa0h/db3e6b79b1010081916ee6498b10c297/image2.png\" alt=\"\" class=\"kg-image\" width=\"1999\" height=\"942\" loading=\"lazy\"/>\n </figure><p><sup><i>Figure 2: Block diagram of BMC in a server system.</i></sup></p><p>At Cloudflare, we use the <a href=\"https://github.com/openbmc/openbmc\"><u>OpenBMC</u></a> project for our Baseboard Management Controller (BMC).</p><p>Below are examples of management functions carried out on a server through the BMC. The interactions in the examples are done over <a href=\"https://github.com/ipmitool/ipmitool/wiki\"><u>ipmitool</u></a>, a command line utility for interacting with systems that support IPMI.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\"># Check the sensor readings of a server remotely (i.e. over a network)\n$ ipmitool &lt;some authentication&gt; &lt;bmc ip&gt; sdr\nPSU0_CURRENT_IN | 0.47 Amps | ok\nPSU0_CURRENT_OUT | 6 Amps | ok\nPSU0_FAN_0 | 6962 RPM | ok\nSYS_FAN | 13034 RPM | ok\nSYS_FAN1 | 11172 RPM | ok\nSYS_FAN2 | 11760 RPM | ok\nCPU_CORE_VR_POUT | 9.03 Watts | ok\nCPU_POWER | 76.95 Watts | ok\nCPU_SOC_VR_POUT | 12.98 Watts | ok\nDIMM_1_VR_POUT | 29.03 Watts | ok\nDIMM_2_VR_POUT | 27.97 Watts | ok\nCPU_CORE_MOSFET | 40 degrees C | ok\nCPU_TEMP | 50 degrees C | ok\nDIMM_MOSFET_1 | 36 degrees C | ok\nDIMM_MOSFET_2 | 39 degrees C | ok\nDIMM_TEMP_A1 | 34 degrees C | ok\nDIMM_TEMP_B1 | 33 degrees C | ok\n\n…\n\n# check the power status of a server remotely (i.e. over a network)\nipmitool &lt;some authentication&gt; &lt;bmc ip&gt; power status\nChassis Power is off\n\n# power on the server\nipmitool &lt;some authentication&gt; &lt;bmc ip&gt; power on\nChassis Power Control: On</pre></code>\n <p>Switching to OpenBMC firmware for our BMCs gives us more control over the software that powers our infrastructure. This has given us more flexibility, customizations, and an overall better uniform experience for managing our servers. Since OpenBMC is open source, we also leverage community fixes while upstreaming some of our own. Some of the advantages we have experienced with OpenBMC include a faster turnaround time to fixing issues, <a href=\"https://blog.cloudflare.com/de-de/thermal-design-supporting-gen-12-hardware-cool-efficient-and-reliable/\"><u>optimizations around thermal cooling</u></a>, <a href=\"https://blog.cloudflare.com/gen-12-servers/\"><u>increased power efficiency</u></a> and <a href=\"https://blog.cloudflare.com/how-we-used-openbmc-to-support-ai-inference-on-gpus-around-the-world/\"><u>supporting AI inference</u></a>.</p><p>While developing Cloudflare’s OpenBMC firmware, however, we ran into a number of boot problems.</p><p><b><i>Host not booting:</i></b> When we send a request over IPMI for a host to power on (as in the example above, power on the server), ipmitool would indicate the power status of the host as ON, but we would not see any power going into the CPU nor any activity on the CPU. While ipmitool was correct about the power going into the chassis as ON, we had no information about the power state of the server from ipmitool, and we initially falsely assumed that since the chassis power was on, the rest of the server components should be ON. The <a href=\"https://documents.uow.edu.au/~blane/netapp/ontap/sysadmin/monitoring/concept/c_oc_mntr_bmc-sys-event-log.html\"><u>System Event Log (SEL)</u></a>, which is responsible for displaying platform-specific events, was not giving us any useful information beyond indicating that the server was in a soft-off state (powered off), working state (operating system is loading and running), or that a “System Restart” of the host was initiated.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\"># System Event Logs (SEL) showing the various power states of the server\n$ ipmitool sel elist | tail -n3\n 4d | Pre-Init |0000011021| System ACPI Power State ACPI_STATUS | S5_G2: soft-off | Asserted\n 4e | Pre-Init |0000011022| System ACPI Power State ACPI_STATUS | S0_G0: working | Asserted\n 4f | Pre-Init |0000011023| System Boot Initiated RESTART_CAUSE | System Restart | Asserted</pre></code>\n <p>In the System Event Logs shown above, ACPI is the acronym for Advanced Configuration and Power Interface, a standard for power management on computing systems. In the ACPI soft-off state, the host is powered off (the motherboard is on standby power but CPU/host isn’t powered on); according to the <a href=\"https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf\"><u>ACPI specifications</u></a>, this state is called S5_G2. (These states are discussed in more detail below.) In the ACPI working state, the host is booted and in a working state, also known in the ACPI specifications as status S0_G0 (which in our case happened to be false), and the third row indicates the cause of the restart was due to a System Restart. Most of the boot-related SEL events are sent from the UEFI to the BMC. The UEFI has been something of a black box to us, as we rely on our original equipment manufacturers (OEMs) to develop the UEFI firmware for us, and for the generation of servers with this issue, the UEFI firmware did not implement sending the boot progress of the host to the BMC.</p><p>One discrepancy we observed was the difference in the power status and the power going into the CPU, which we read with a sensor we call CPU_POWER.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\"># Check power status\n$ ipmitool &lt;some authentication&gt; &lt;bmc ip&gt; power status\nChassis Power is on\n</pre></code>\n <p>However, checking the power into the CPU shows that the CPU was not receiving any power.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\"># Check power going into the CPU\n$ ipmitool &lt;some authentication&gt; &lt;bmc ip&gt; sdr | grep CPU_POWER \nCPU_POWER | 0 Watts | ok</pre></code>\n <p>The CPU_POWER being at 0 watts contradicts all the previous information that the host was powered up and working, when the host was actually completely shut down.</p><p><b><i>Missing Memory Modules:</i></b> Our servers would randomly boot up with less memory than expected. Computers can boot up with less memory than installed due to a number of problems, such as a loose connection, hardware problem, or faulty memory. For our case, it happened not to be any of the usual suspects, but instead was due to both the BMC and UEFI trying to simultaneously read from the memory modules, leading to access contentions. Memory modules usually contain a <a href=\"https://en.wikipedia.org/wiki/Serial_presence_detect\"><u>Serial Presence Detect (SPD)</u></a>, which is used by the UEFI to dynamically detect the memory module. This SPD is usually located on an <a href=\"https://learn.sparkfun.com/tutorials/i2c/all\"><u>inter-integrated circuit (i2c)</u></a>, which is a low speed, two write protocol for devices to talk to each other. The BMC also reads the temperature of the memory modules via the i2c. When the server is powered on, amongst other hardware initializations, the UEFI also initializes the memory modules that it can detect via their (i.e. each individual memory modules) Serial Presence Detect (SPD), the BMC could also be trying to access the temperature of the memory module at the same time, over the same i2c protocol. This simultaneous attempted read denies one of the parties access. When the UEFI is denied access to the SPD, it thinks the memory module is not available and skips over it. Below is an example of the related i2c-bus contention logs we saw in the <a href=\"https://www.freedesktop.org/software/systemd/man/latest/journalctl.html\"><u>journal</u></a> of the BMC when the host is booting.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\">kernel: aspeed-i2c-bus 1e78a300.i2c-bus: irq handled != irq. expected 0x00000021, but was 0x00000020</pre></code>\n <p>The above logs indicate that the i2c address 1e78a300 (which happens to be connected to the serial presence detect of the memory modules) could not properly handle a signal, known as an interrupt request (irq). When this scenario plays out on the UEFI, the UEFI is unable to detect the memory module.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Fe8wb6xqwXkanb8iPv8O2/eaecfe0474576a00cdc25bfeb6fba7a2/image4.png\" alt=\"\" class=\"kg-image\" width=\"624\" height=\"523\" loading=\"lazy\"/>\n </figure><p><sup><i>Figure 3: I2C diagram showing I2C interconnection of the server’s memory modules (also known as DIMMs) with the BMC </i></sup></p><p><a href=\"https://www.techtarget.com/searchstorage/definition/DIMM\"><u>DIMM</u></a> in Figure 3 refers to <a href=\"https://www.techtarget.com/searchstorage/definition/DIMM\"><u>Dual Inline Memory Module</u></a>, which is the type of memory module used in servers.</p><p><b><i>Thermal telemetry:</i></b> During the boot-up process of some of our servers, some temperature devices, such as the temperature sensors of the memory modules, would show up as failed, thereby causing some of the fans to enter a fail-safe <a href=\"https://en.wikipedia.org/wiki/Pulse-width_modulation\"><u>Pulse Width Modulation (PWM)</u></a> mode. <a href=\"https://en.wikipedia.org/wiki/Pulse-width_modulation\"><u>PWM</u></a> is a technique to encode information delivered to electronic devices by adjusting the frequency of the waveform signal to the device. It is used in this case to control fan speed by adjusting the frequency of the power signal delivered to the fan. When a fan enters a fail-safe mode, PWM is used to set the fan speeds to a preset value, irrespective of what the optimized PWM setting of the fans should be, and this could negatively affect the cooling of the server and power consumption.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"implementing-host-acpi-state-on-openbmc\">Implementing host ACPI state on OpenBMC</h2>\n <a href=\"#implementing-host-acpi-state-on-openbmc\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In the process of studying the issues we faced relating to the boot-up process of the host, we learned how the power state of the subsystems within the chassis changes. Part of our learnings led us to investigate the Advanced Configuration and Power Interface (ACPI) and how the ACPI state of the host changed during the boot process.</p><p>Advanced Configuration and Power Interface (ACPI) is an open industry specification for power management used in desktop, mobile, workstation, and server systems. The <a href=\"https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf\"><u>ACPI Specification</u></a> replaces previous power management methodologies such as <a href=\"https://en.wikipedia.org/wiki/Advanced_Power_Management\"><u>Advanced Power Management (APM)</u></a>. ACPI provides the advantages of:</p><ul><li><p>Allowing OS-directed power management (OSPM).</p></li><li><p>Having a standardized and robust interface for power management.</p></li><li><p>Sending system-level events such as when the server power/sleep buttons are pressed </p></li><li><p>Hardware and software support, such as a real-time clock (RTC) to schedule the server to wake up from sleep or to reduce the functionality of the CPU based on RTC ticks when there is a loss of power.</p></li></ul><p>From the perspective of power management, ACPI enables an OS-driven conservation of energy by transitioning components which are not in active use to a lower power state, thereby reducing power consumption and contributing to more efficient power management.</p><p>The ACPI Specification defines four global “Gx” states, six sleeping “Sx” states, and four “Dx” device power states. These states are defined as follows:</p><div style=\"margin-left:0pt;\" dir=\"ltr\" align=\"left\">\n <figure class=\"table\">\n <table class=\"ck-table-resized\" style=\"border-collapse:collapse;border-style:none;\">\n <colgroup>\n <col style=\"width:25%;\" width=\"64\">\n <col style=\"width:25%;\" width=\"248\">\n <col style=\"width:25%;\" width=\"53\">\n <col style=\"width:25%;\" width=\"259\">\n </colgroup>\n <tbody>\n <tr style=\"height:0pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Gx</strong></span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Name</strong></span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Sx</strong></span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Description</strong></span></span></p>\n </td>\n </tr>\n <tr style=\"height:0pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">G0</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Working</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S0</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">The run state. In this state the machine is fully running</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" rowspan=\"4\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">G1</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" rowspan=\"4\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Sleeping</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S1</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.3800000000000001;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">A sleep state where the CPU will suspend activity but retain its contexts.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S2</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">A sleep state where memory contexts are held, but CPU contexts are lost. CPU re-initialization is done by firmware.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S3</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">A logically deeper sleep state than S2 where CPU re-initialization is done by device. Equates to Suspend to RAM.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S4</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">A logically deeper sleep state than S3 in which DRAM is context is not maintained and contexts are saved to disk. Can be implemented by either OS or firmware.&nbsp;</span></span></p>\n </td>\n </tr>\n <tr style=\"height:0pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">G2</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Soft off but PSU still supplies power</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">S5</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">The soft off state. All activity will stop, and all contexts are lost. The Complex Programmable Logic Device (CPLD) responsible for power-up and power-down sequences of various components e.g. CPU, BMC is on standby power, but the CPU/host is off.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:0pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">G3</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Mechanical off</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">&nbsp;</td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">PSU does not supply power. The system is safe for disassembly.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Dx</strong></span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Name</strong></span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" colspan=\"2\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\"><strong>Description</strong></span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">D0</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Fully powered on</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" colspan=\"2\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Hardware device is fully functional and operational&nbsp;</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">D1</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Hardware device is partially powered down</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" colspan=\"2\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Reduced functionality and can be quickly powered back to D0</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">D2</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Hardware device is in a deeper lower power than D1</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" colspan=\"2\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Much more limited functionality and can only be slowly powered back to D0.</span></span></p>\n </td>\n </tr>\n <tr style=\"height:21pt;\">\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">D3</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Hardware device is significantly powered down or off</span></span></p>\n </td>\n <td style=\"border-color:#000000;border-width:1pt;overflow-wrap:break-word;overflow:hidden;padding:5pt;vertical-align:top;\" colspan=\"2\">\n <p style=\"line-height:1.2;margin-bottom:0pt;margin-top:0pt;\" dir=\"ltr\"><span style=\"background-color:transparent;color:#000000;font-family:Arial,sans-serif;\"><span style=\"font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;\">Device is inactive with perhaps only the ability to be powered back on</span></span></p>\n </td>\n </tr>\n </tbody>\n </table>\n </figure>\n</div><p>The states that matter to us are:</p><ul><li><p><b>S0_G0_D0:</b> often referred to as the working state. Here we know our host system is running just fine.</p></li><li><p><b>S2_D2: </b>Memory contexts are held, but CPU context is lost. We usually use this state to know when the host’s UEFI is performing platform firmware initialization.</p></li><li><p><b>S5_G2:</b> Often referred to as the soft off state. Here we still have power going into the chassis, however, processor and DRAM context are not maintained, and the operating system power management of the host has no context.</p></li></ul><p>Since the issues we were experiencing were related to the power state changes of the host — when we asked the host to reboot or power on — we needed a way to track the various power state changes of the host as it went from power off to a complete working state. This would give us better management capabilities over the devices that were on the same power domain of the host during the boot process. Fortunately, the OpenBMC community already implemented an <a href=\"https://github.com/openbmc/google-misc/tree/master/subprojects/acpi-power-state-daemon\"><u>ACPI daemon</u></a>, which we extended to serve our needs. We added an ACPI S2_D2 power state, in which memory contexts are held, but CPU context is lost, to the ACPI daemon running on the BMC to enable us to know when the host’s UEFI is performing firmware initialization, and also set up various management tasks for the different ACPI power states.</p><p>An example of a power management task we carry out using the S0_G0_D0 state is to re-export our Voltage Regulator (VR) sensors on S0_G0_D0 state, as shown with the service file below:</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\">cat /lib/systemd/system/Re-export-VR-device.service \n[Unit]\nDescription=RE Export VR Device Process\nWants=xyz.openbmc_project.EntityManager.service\nAfter=xyz.openbmc_project.EntityManager.service\nConflicts=host-s2-state.target\n\n[Service]\nType=simple\nExecStart=/bin/bash -c &#039;set -a &amp;&amp; source /usr/bin/Re-export-VR-device.sh on&#039;\nSyslogIdentifier=Re-export-VR-device.service\n\n[Install]\nWantedBy=host-s0-state.target\n</pre></code>\n <p>Having set this up, OpenBMC has a Net Function (ipmiSetACPIState) in <a href=\"https://github.com/openbmc/phosphor-host-ipmid/tree/master\"><u>phosphor-host-ipmid</u></a> that is responsible for setting the ACPIState of the host on the BMC. This command is called by the host using the standard ipmi command with the corresponding NetFn=0x06 and Cmd=0x06.</p><p>In the event of an immediate power cycle (i.e. host reboots without operating system shutdown), the host is unable to send its S5_G2 state to the BMC. For this case, we created a patch to OpenBMC’s <a href=\"https://github.com/openbmc/x86-power-control/tree/master\"><u>x86-power-control</u></a> to let the BMC become aware that the host has entered the ACPI S5_G2 state (i.e. soft-off). When the host comes out of the power off state, the UEFI performs the Power On Self Test (POST) and sends the S2_D2 to the BMC, and after the UEFI has loaded the OS on the host, it notifies the BMC by sending the ACPI S0_G0_D0 state.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"fixing-the-issues\">Fixing the issues</h2>\n <a href=\"#fixing-the-issues\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Going back to the boot-up issues we faced, we discovered that they were mostly caused by devices which were in the same power domain of the CPU, interfering with the UEFI/platform firmware initialization phase. Below is a high level description of the fixes we applied.</p><p><b><i>Servers not booting</i></b><b>:</b> After identifying the devices that were interfering with the POST stage of the firmware initialization, we used the host ACPI state to control when we set the appropriate power mode state for those devices so as not to cause POST to fail.</p><p><b><i>Memory modules missing</i></b><b>:</b> During the boot-up process, memory modules (DIMMs) are powered and initialized in S2_D2 ACPI state. During this initialization process, UEFI firmware sends read commands to the Serial Presence Detect (SPD) on the DIMM to retrieve information for DIMM enumeration. At the same time, the BMC could be sending commands to read DIMM temperature sensors. This can cause SMBUS collisions, which could either cause DIMM temperature reading to fail or UEFI DIMM enumeration to fail. The latter case would cause the system to boot up with reduced DIMM capacity, which could be mistaken as a failing DIMM scenario. After we had discovered the race condition issue, we disabled the BMC from reading the DIMM temperature sensors during S2_D2 ACPI state and set a fixed speed for the corresponding fans. This solution allows our UEFI to retrieve all the necessary DIMM subsystems information for enumeration, and our servers now boot up with the correct size of memory.</p><p><b>Thermal telemetry:</b> In S0_G0 power state, when sensors are not reporting values back to the BMC, the BMC assumes that devices may be overheating and puts the fan controller into fail-safe mode where fan speeds are ramped up to maximum speed. However, in S5_G2 state, some thermal sensors like CPU temperature, NIC temperature, etc. are not powered and not available. Our solution is to set these thermal sensors as non-functional in their exported configuration when in S5_G2 state and during the transition from S5_G2 state to S2_D2 state. Setting the affected devices as non-functional in their configuration, instead of waiting for thermal sensor read commands to error out, prevents the controller from entering the fail-safe mode.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"moving-forward\">Moving forward</h2>\n <a href=\"#moving-forward\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Aside from resolving issues, we have seen other benefits from implementing ACPI Power State on our BMC firmware. An example is in the area of our automated firmware regression testing. Various parts of our tests require rebooting/power cycling the servers over a hundred times, during which we monitor the ACPI power state changes of our servers as against using a boolean (running or not running, pingable or not pingable) to assert the status of our servers.</p><p>Also, it has given us the opportunity to learn more about the complex subsystems in a server system, and the various power modes of the different subsystems. This is an aspect that we are still actively learning about as we look to further optimize various aspects of the boot sequence of our servers.</p><p>In the course of time, implementing ACPI states is helping us achieve the following:</p><ul><li><p>All components are enabled by end of boot sequence,</p></li><li><p>BIOS and BMC are able to retrieve component information,</p></li><li><p>And the BMC is aware when thermal sensors are in a non-functional state.\n</p></li></ul><p>For better observability of the boot progress and “last state” of our systems, we have also started the process of adding the BootProgress object of the <a href=\"https://redfish.dmtf.org/schemas/v1/ComputerSystem.v1_13_0.json\"><u>Redfish ComputerSystem Schema</u></a> into our systems. This will give us an opportunity for pre-operating system (OS) boot observability and an easier debug starting point when the UEFI has issues (such as when the server isn’t coming on) during the server platform initialization.</p><p>With each passing day, Cloudflare’s OpenBMC team, which is made up of folks from different embedded backgrounds, learns about, experiments with, and deploys OpenBMC across our global fleet. This has been made possible by relying on the OpenBMC community’s contribution (as well as upstreaming some of our own contributions), and our interaction with our various vendors, thereby giving us the opportunity to make our systems more reliable, and giving us the ownership and responsibility of the firmware that powers the BMCs that manage our servers. If you are thinking of embracing open-source firmware in your BMC, we hope this blog post written by a team which started deploying OpenBMC less than 18 months ago has inspired you to give it a try. </p><p>For those who are interested in considering making the jump to open-source firmware, check it out <a href=\"https://github.com/openbmc/openbmc\"><u>here</u></a>!</p>"],"published_at":[0,"2024-10-22T14:00+01:00"],"updated_at":[0,"2024-10-22T14:29:45.868Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tXffZR8RhNdDz7mqsydir/41a33236f8eac2ca38d4b5ae9c21a77e/image3.png"],"tags":[1,[[0,{"id":[0,"7fYeYFd6aJXluz5xmGkdZT"],"name":[0,"Infrastructure"],"slug":[0,"infrastructure"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"2zGeaSPj2uoPFugy8J60n1"],"name":[0,"OpenBMC"],"slug":[0,"open-bmc"]}],[0,{"id":[0,"1IVpRmO1Bg0J9pDI7FUeEB"],"name":[0,"Servers"],"slug":[0,"servers"]}],[0,{"id":[0,"vGIiidDZ4NKOzDrDSfIjN"],"name":[0,"Firmware"],"slug":[0,"firmware"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Nnamdi Ajah"],"slug":[0,"nnamdi"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3FssFdDxuBmbKbJiY4PuNj/e36d48a362480cbc7e38b1017290ad69/nnamdi.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Ryan Chow"],"slug":[0,"ryan-chow"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5TJGoRJtGt1tLdEfiXF5Zn/3d7cda2f3b5cacd67b8bb1720d8dcc40/ryan-chow.png"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Giovanni Pereira Zantedeschi"],"slug":[0,"giovanni"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7A7A57tAXHrOynxnx3eFpi/15cfa82b216391c547be01178e30cd98/giovanni.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"Cloudflare’s global fleet benefits from being managed by open source firmware for the Baseboard Management Controller (BMC), OpenBMC. This has come with various challenges, some of which we discuss here with an explanation of how the open source nature of the firmware for the BMC enabled us to fix the issues and maintain a more stable fleet."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"blog-english-only"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/how-we-use-openbmc-and-acpi-power-states-to-monitor-the-state-of-our-servers"],"metadata":[0,{"title":[0,"Is this thing on? Using OpenBMC and ACPI power states for reliable server boot"],"description":[0,"Cloudflare’s global fleet benefits from being managed by open source firmware for the Baseboard Management Controller (BMC), OpenBMC. This has come with various challenges, some of which we discuss here with an explanation of how the open source nature of the firmware for the BMC enabled us to fix the issues and maintain a more stable fleet."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/LZdIivdHwtFqTFXKQ5uDD/ae1874bac4f578597a346ade6a24f248/Is_this_thing_on"]}]}],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"PostCard","value":true}" await-children=""> <article class="w-100 featured-post flex flex-row flex-wrap mb4 items-center bb b--gray8 bn-l mt4 mt2-l mb4 ph3 bb b--gray8 bn-l"> <div class="w-50-l"> <a href="https://blog-cloudflare-com.translate.goog/how-we-use-openbmc-and-acpi-power-states-to-monitor-the-state-of-our-servers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 no-underline gray1" data-testid="post-title"><h2 class="fw5 mt2">Is this thing on? Using OpenBMC and ACPI power states for reliable server boot</h2></a> <p class="f3 fw5 gray5 my" data-testid="post-date">2024-10-22</p> <p class="f4 fw3 lh-copy " data-testid="post-content">Cloudflare’s global fleet benefits from being managed by open source firmware for the Baseboard Management Controller (BMC), OpenBMC. This has come with various challenges, some of which we discuss here with an explanation of how the open source nature of the firmware for the BMC enabled us to fix the issues and maintain a more stable fleet.<!-- -->...</p><a href="https://blog-cloudflare-com.translate.goog/how-we-use-openbmc-and-acpi-power-states-to-monitor-the-state-of-our-servers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 lh-copy fw3 underline-hover" data-testid="post-continue-reading">Continue reading »</a> <ul class="author-lists flex pl0"> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/nnamdi?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3FssFdDxuBmbKbJiY4PuNj/e36d48a362480cbc7e38b1017290ad69/nnamdi.jpg" alt="Nnamdi Ajah" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/nnamdi?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f4 no-underline black">Nnamdi Ajah</a> </div></li> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/ryan-chow?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5TJGoRJtGt1tLdEfiXF5Zn/3d7cda2f3b5cacd67b8bb1720d8dcc40/ryan-chow.png" alt="Ryan Chow" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/ryan-chow?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f4 no-underline black">Ryan Chow</a> </div></li> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/giovanni?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7A7A57tAXHrOynxnx3eFpi/15cfa82b216391c547be01178e30cd98/giovanni.jpg" alt="Giovanni Pereira Zantedeschi" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/giovanni?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f4 no-underline black">Giovanni Pereira Zantedeschi</a> </div></li> </ul> </div> <div class="w-50-l"> <img class="dn di-l " src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tXffZR8RhNdDz7mqsydir/41a33236f8eac2ca38d4b5ae9c21a77e/image3.png" alt="Is this thing on? Using OpenBMC and ACPI power states for reliable server boot"> </div> </article><!--astro:end--> </astro-island><astro-island uid="RkjoS" prefix="r1" component-url="/_astro/PostCard.DWAKGH4e.js" component-export="PostCard" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"currentPage":[0,1],"isFeaturedImageFirstPost":[0,false],"post":[0,{"id":[0,"5LrF3eCtonOcP2Sf5BSVpe"],"title":[0,"Expanding Cloudflare's support for open source projects with Project Alexandria"],"slug":[0,"expanding-our-support-for-oss-projects-with-project-alexandria"],"excerpt":[0,"At Cloudflare, we believe in the power of open source. With Project Alexandria, our expanded open source program, we’re helping open source projects have a sustainable and scalable future, providing them with the tools and protection needed to thrive."],"featured":[0,false],"html":[0,"<p>At Cloudflare, we believe in the power of open source. It’s more than just code, it’s the spirit of collaboration, innovation, and shared knowledge that drives the Internet forward. Open source is the foundation upon which the Internet thrives, allowing developers and creators from around the world to contribute to a greater whole.</p><p>But oftentimes, open source maintainers struggle with the costs associated with running their projects and providing access to users all over the world. We’ve had the privilege of supporting incredible open source projects such as <a href=\"https://git-scm.com/\"><u>Git</u></a> and the <a href=\"https://www.linuxfoundation.org/\"><u>Linux Foundation</u></a> through our <a href=\"https://blog.cloudflare.com/cloudflare-new-oss-sponsorships-program/\"><u>open source program</u></a> and learned first-hand about the places where Cloudflare can help the most.</p><p>Today, we&#39;re introducing a streamlined and expanded open source program: Project Alexandria. The ancient city of Alexandria is known for hosting a prolific library and a lighthouse that was one of the Seven Wonders of the Ancient World. The Lighthouse of Alexandria served as a beacon of culture and community, welcoming people from afar into the city. We think Alexandria is a great metaphor for the role open source projects play as a beacon for developers around the world and a source of knowledge that is core to making a better Internet. </p><p>This project offers recurring annual credits to even more open source projects to provide our products for free. In the past, we offered an upgrade to our Pro plan, but now we’re offering upgrades tailored to the size and needs of each project, along with access to a broader range of products like <a href=\"https://workers.cloudflare.com/\"><u>Workers</u></a>, <a href=\"https://pages.cloudflare.com/\"><u>Pages</u></a>, and more. Our goal with Project Alexandria is to ensure every OSS project not only survives but thrives, with access to Cloudflare’s enhanced security, performance optimization, and developer tools — all at no cost.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"building-a-program-based-on-your-needs\">Building a program based on your needs</h2>\n <a href=\"#building-a-program-based-on-your-needs\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We realize that open source projects have different needs. Some projects, like package repositories, may be most concerned about storage and transfer costs. Other projects need help protecting them from DDoS attacks. And some projects need a robust developer platform to enable them to quickly build and deploy scalable and secure applications.</p><p>With our new program we’ll work with your project to help unlock the following based on your needs:</p><ul><li><p>An upgrade to a Cloudflare Pro, Business, or Enterprise plan, which will give you more flexibility with more <a href=\"https://developers.cloudflare.com/rules/\"><u>Cloudflare Rules</u></a> to manage traffic with, Image Optimization with <a href=\"https://developers.cloudflare.com/images/polish/\"><u>Polish</u></a> to accelerate the speed of image downloads, and enhanced security with <a href=\"https://www.cloudflare.com/en-gb/application-services/products/waf/\"><u>Web Application Firewall (WAF)</u></a>, <a href=\"https://developers.cloudflare.com/waf/analytics/security-analytics/\"><u>Security Analytics</u></a>, and <a href=\"https://developers.cloudflare.com/page-shield/\"><u>Page Shield</u></a>, to protect projects from potential threats and vulnerabilities.</p></li><li><p>Increased requests to Cloudflare <a href=\"https://workers.cloudflare.com/\"><u>Workers</u></a> and <a href=\"https://pages.cloudflare.com/\"><u>Pages</u></a>, allowing you to handle more traffic and scale your applications globally.</p></li><li><p>Increased <a href=\"https://developers.cloudflare.com/r2/\"><u>R2</u></a> storage for builds and artifacts, ensuring you have the space needed to store and access your project’s assets efficiently.</p></li><li><p>Enhanced <a href=\"https://developers.cloudflare.com/cloudflare-one/\"><u>Zero Trust</u></a> access, including <a href=\"https://developers.cloudflare.com/cloudflare-one/policies/browser-isolation/\"><u>Remote Browser Isolation</u></a>, no user limits, and extended activity log retention to give you deeper insights and more control over your project’s security.</p></li></ul><p>Every open source project in the program will receive additional resources and support through a dedicated <a href=\"https://discord.com/channels/595317990191398933/1284158129474506802\"><u>channel</u></a> on our <a href=\"https://discord.cloudflare.com\"><u>Discord server</u></a>. And if there’s something you think we can do to help that we don’t currently offer, we’re here to figure out how to make it happen.</p><p>Many open source projects run within the limits of Cloudflare’s generous <a href=\"https://www.cloudflare.com/en-gb/plans/\"><u>free tiers</u></a>. Our mission to help build a better Internet means that cost should not be a barrier to creating, securing, and distributing your open source packages globally, no matter the size of the project. Indie or niche open source projects can still run for free without the need for credits. For larger open source projects, the annual recurring credits are available to you, so your money can continue to be reinvested into innovation, instead of paying for infrastructure to store, secure, and deliver your packages and websites. </p><p>We’re dedicated to supporting projects that are not only innovative but also crucial to the continued growth and health of the internet. The criteria for the program remain the same:</p><ul><li><p>Operate solely on a non-profit basis and/or otherwise align with the project mission.</p></li><li><p>Be an open source project with a <a href=\"https://opensource.org/licenses/\"><u>recognized OSS license</u></a>.</p></li></ul><p>If you’re an open source project that meets these requirements, you can <a href=\"https://www.cloudflare.com/lp/project-alexandria/\"><u>apply for the program here</u></a>.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"empowering-the-open-source-community\">Empowering the Open Source community</h2>\n <a href=\"#empowering-the-open-source-community\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We’re incredibly lucky to have open source projects that we admire, and the incredible people behind those <a href=\"https://developers.cloudflare.com/sponsorships/\"><u>projects</u></a>, as part of our program — including the <a href=\"https://openjsf.org/\"><u>OpenJS Foundation</u></a>, <a href=\"https://opentofu.org/\"><u>OpenTofu</u></a>, and <a href=\"https://julialang.org/\"><u>JuliaLang</u></a>.</p><p><b>OpenJS Foundation</b></p><p><a href=\"https://github.com/nodejs\"><u>Node.js</u></a> has been part of our OSS Program since 2019, and we’ve recently partnered with the <a href=\"https://openjsf.org/\"><u>OpenJS Foundation</u></a> to provide technical support and infrastructure improvements to other critical JavaScript projects hosted at the foundation, including <a href=\"https://github.com/fastify/fastify\"><u>Fastify</u></a>, <a href=\"https://github.com/jquery/jquery\"><u>jQuery</u></a>, <a href=\"https://github.com/electron/electron\"><u>Electron</u></a>, and <a href=\"https://github.com/NativeScript/NativeScript\"><u>NativeScript</u></a>.</p><p>One prominent example of the<a href=\"https://openjsf.org/\"><u> OpenJS Foundation</u></a> using Cloudflare is the Node.js CDN Worker. It’s currently in active development by the Node.js Web Infrastructure and Build teams and aims to serve all Node.js release assets (binaries, documentations, etc.) provided on their website. </p><p><a href=\"https://x.com/NodeConfEU/status/1823676122648715581\"><u>Aaron Snell</u></a> explained that these release assets are currently being served by a single static origin file server fronted by Cloudflare. This worked fine up until a few years ago when issues began to pop up with new releases. With a new release came a cache purge, meaning that all the requests for the release assets were cache misses, causing Cloudflare to go forward directly to the static file server, overloading it. Because Node.js releases nightly builds, this issue occurs every day.</p><p>The CDN Worker plans to fix this by using Cloudflare Workers and R2 to serve requests for the release assets, taking all the load off the static file server, resulting in improved availability for Node.js downloads and documentation, and ultimately making the process more sustainable in the long run.</p><p><b>OpenTofu</b></p><p><a href=\"https://github.com/opentofu/opentofu\"><u>OpenTofu</u></a> has been focused on building a free and open alternative to proprietary infrastructure-as-code platforms. One of their major challenges has been ensuring the reliability and scalability of their registry while keeping costs low. Cloudflare&#39;s <a href=\"https://developers.cloudflare.com/r2/\"><u>R2</u></a> storage and caching services provided the perfect fit, allowing <a href=\"https://github.com/opentofu/opentofu\"><u>OpenTofu</u></a> to serve static files at scale without worrying about bandwidth or performance bottlenecks.</p><p>The OpenTofu team noted that it was paramount for OpenTofu to keep the costs of running the registry as low as possible both in terms of bandwidth and also in human cost. However, they also needed to make sure that the registry had an uptime close to 100% since thousands upon thousands of developers would be left without a means to update their infrastructure if it went down.</p><p>The registry codebase (written in Go) pre-generates all possible answers of the OpenTofu Registry API and uploads the static files to an R2 bucket. With R2, OpenTofu has been able to run the registry essentially for free with no servers and scaling issues to worry about.</p><p><b>JuliaLang</b></p><p><a href=\"https://github.com/JuliaLang/julia\"><u>JuliaLang</u></a> has recently joined our OSS Sponsorship Program, and we’re excited to support their critical infrastructure to ensure the smooth operation of their ecosystem. A key aspect of this support is enabling the use of Cloudflare’s services to help <a href=\"https://github.com/JuliaLang/julia\"><u>JuliaLang</u></a> deliver packages to its user base.</p><p>According to <a href=\"https://staticfloat.github.io/\"><u>Elliot Saba</u></a>, JuliaLang had been using Amazon Lightsail as a cost-effective global CDN to serve packages to their user base. However, as their user base grew they would occasionally exceed their bandwidth limits and rack up serious cloud costs, not to mention experiencing degraded performance due to load balancer VMs getting overloaded by traffic spikes. Now JuliaLang is using Cloudflare <a href=\"https://developers.cloudflare.com/r2/\"><u>R2</u></a>, and the speed and reliability of R2 object storage has so far exceeded that of their own within-datacenter solutions, and the lack of bandwidth charges means JuliaLang is now getting faster, more reliable service for less than a tenth of their previous spend.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-can-we-help\">How can we help?</h2>\n <a href=\"#how-can-we-help\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>If your project fits our criteria, and you’re looking to reduce costs and eliminate surprise bills, we invite you to apply! We’re eager to help the next generation of open source projects make their mark on the internet.</p><p>For more details and to apply, visit our new <a href=\"https://www.cloudflare.com/lp/project-alexandria/\"><u>Project Alexandria page</u></a>. And if you know other projects that could benefit from this program, please spread the word!</p>"],"published_at":[0,"2024-09-27T14:00+01:00"],"updated_at":[0,"2024-10-10T14:29:41.413Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6wgIGPJ07w9SfXaZTHQe4v/e553f2244b5726901b3eae72bb28ef5c/image1.png"],"tags":[1,[[0,{"id":[0,"1Cv5JjXzKWKEA10JdYbXu1"],"name":[0,"Birthday Week"],"slug":[0,"birthday-week"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"3Fo5JgRZ6Fh2HHzhEr7AKn"],"name":[0,"Better Internet"],"slug":[0,"better-internet"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Veronica Marin"],"slug":[0,"veronica-marin"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IDGPzZgmO090QJb7RP1By/d26846f1c5e42543d8c1c8a5108fda8b/veronica-marin.png"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Gabby Shires"],"slug":[0,"gabby-shires"],"bio":[0],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lP5NuQZrbE96SNIEn2OOm/5c4da49e489bdb048a46f1f022bb8cb2/unnamed.webp"],"location":[0],"website":[0],"twitter":[0],"facebook":[0]}]]],"meta_description":[0,"At Cloudflare, we believe in the power of open source. With Project Alexandria, our expanded open source program, we’re helping open source projects have a sustainable and scalable future, providing them with the tools and protection needed to thrive."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"BLOG-2515 LOC List - deDE frFR esES zhCN jaJP zhTW koKR"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"Translated for Locale"],"frFR":[0,"Translated for Locale"],"deDE":[0,"Translated for Locale"],"itIT":[0,"English for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"Translated for Locale"],"ptBR":[0,"English for Locale"],"esLA":[0,"English for Locale"],"esES":[0,"Translated for Locale"],"enAU":[0,"English for Locale"],"enCA":[0,"English for Locale"],"enIN":[0,"English for Locale"],"enGB":[0,"English for Locale"],"idID":[0,"English for Locale"],"ruRU":[0,"English for Locale"],"svSE":[0,"English for Locale"],"viVN":[0,"English for Locale"],"plPL":[0,"English for Locale"],"arAR":[0,"English for Locale"],"nlNL":[0,"English for Locale"],"thTH":[0,"English for Locale"],"trTR":[0,"English for Locale"],"heIL":[0,"English for Locale"],"lvLV":[0,"English for Locale"],"etEE":[0,"English for Locale"],"ltLT":[0,"English for Locale"]}],"url":[0,"https://blog.cloudflare.com/expanding-our-support-for-oss-projects-with-project-alexandria"],"metadata":[0,{"title":[0,"Expanding Cloudflare’s support for open source projects with Project Alexandria"],"description":[0,"At Cloudflare, we believe in the power of open source. With Project Alexandria, our expanded open source program, we’re helping open source projects have a sustainable and scalable future, providing them with the tools and protection needed to thrive."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5mVMH3mYUna1qsN8V8qdbD/b1e6696341523849480961404e109a22/Expanding_Cloudflare_s_support_for_open_source_projects_with_Project_Alexandria_-OG.png"]}]}],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"PostCard","value":true}" await-children=""> <article class="w-50-l mt4 mt2-l mb4 ph3 bb b--gray8 bn-l"> <div class="w-100"> <a href="https://blog-cloudflare-com.translate.goog/expanding-our-support-for-oss-projects-with-project-alexandria?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 no-underline gray1" data-testid="post-title"><h2 class="fw5 mt2">Expanding Cloudflare's support for open source projects with Project Alexandria</h2></a> <p class="f3 fw5 gray5 my" data-testid="post-date">2024-09-27</p> <div class=""> <a href="https://blog-cloudflare-com.translate.goog/tag/birthday-week?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Birthday Week</a><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Open Source</a><a href="https://blog-cloudflare-com.translate.goog/tag/better-internet?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Better Internet</a> </div> <p class="f3 fw4 gray1 lh-copy " data-testid="post-content">At Cloudflare, we believe in the power of open source. With Project Alexandria, our expanded open source program, we’re helping open source projects have a sustainable and scalable future, providing them with the tools and protection needed to thrive.<!-- -->...</p> <ul class="author-lists flex pl0"> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/veronica-marin?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IDGPzZgmO090QJb7RP1By/d26846f1c5e42543d8c1c8a5108fda8b/veronica-marin.png" alt="Veronica Marin" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/veronica-marin?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Veronica Marin</a> </div></li> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/gabby-shires?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lP5NuQZrbE96SNIEn2OOm/5c4da49e489bdb048a46f1f022bb8cb2/unnamed.webp" alt="Gabby Shires" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/gabby-shires?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Gabby Shires</a> </div></li> </ul> </div> </article><!--astro:end--> </astro-island><astro-island uid="Z1VxLVM" prefix="r2" component-url="/_astro/PostCard.DWAKGH4e.js" component-export="PostCard" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"currentPage":[0,1],"isFeaturedImageFirstPost":[0,false],"post":[0,{"id":[0,"2CqKLNS1jaf5H2j99sDONe"],"title":[0,"A good day to trie-hard: saving compute 1% at a time"],"slug":[0,"pingora-saving-compute-1-percent-at-a-time"],"excerpt":[0,"Pingora handles 35M+ requests per second, so saving a few microseconds per request can translate to thousands of dollars saved on computing costs. In this post, we share how we freed up over 500 CPU cores by optimizing one function and announce trie-hard, the open source crate that we created to do it."],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5uwVNobeSBws457ad5SoNY/080de413142fc98caffc3c0108912fe4/2442-1-hero.png\" alt=\"2442-1-hero\" class=\"kg-image\" width=\"1800\" height=\"1013\" loading=\"lazy\"/>\n </figure><p>Cloudflare’s global network handles <i>a lot</i> of HTTP requests – over 60 million per second on average. That in and of itself is not news, but it is the starting point to an adventure that started a few months ago and ends with the announcement of a new <a href=\"https://github.com/cloudflare/trie-hard\"><u>open-source Rust crate</u></a> that we are using to reduce our CPU utilization, enabling our CDN to handle even more of the world’s ever-increasing Web traffic. </p>\n <div class=\"flex anchor relative\">\n <h2 id=\"motivation\">Motivation</h2>\n <a href=\"#motivation\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Let’s start at the beginning. You may recall a few months ago we released <a href=\"https://blog.cloudflare.com/pingora-open-source/\"><u>Pingora</u></a> (the heart of our Rust-based proxy services) as an <a href=\"https://github.com/cloudflare/pingora\"><u>open-source project on GitHub</u></a>. I work on the team that maintains the Pingora framework, as well as Cloudflare’s production services built upon it. One of those services is responsible for the final step in transmitting users’ (non-cached) requests to their true destination. Internally, we call the request’s destination server its “origin”, so our service has the (unimaginative) name of “pingora-origin”.</p><p>One of the many responsibilities of pingora-origin is to ensure that when a request leaves our infrastructure, it has been cleaned to remove the internal information we use to route, measure, and optimize traffic for our customers. This has to be done for every request that leaves Cloudflare, and as I mentioned above, it’s <i>a lot</i> of requests. At the time of writing, the rate of requests leaving pingora-origin (globally) is 35 million requests per second. Any code that has to be run per-request is in the hottest of hot paths, and it’s in this path that we find this code and comment:</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\">// PERF: heavy function: 1.7% CPU time\npub fn clear_internal_headers(request_header: &amp;mut RequestHeader) {\n INTERNAL_HEADERS.iter().for_each(|h| {\n request_header.remove_header(h);\n });\n}</pre></code>\n <p></p><p>This small and pleasantly-readable function consumes more than 1.7% of pingora-origin’s total cpu time. To put that in perspective, the total cpu time consumed by pingora-origin is 40,000 compute-seconds per second. You can think of this as 40,000 saturated CPU cores fully dedicated to running pingora-origin. Of those 40,000, 1.7% (680) are only dedicated to evaluating <code>clear_internal_headers</code>. The function’s heavy usage and simplicity make it seem like a great place to start optimizing.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"benchmarking\">Benchmarking</h2>\n <a href=\"#benchmarking\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Benchmarking the function shown above is straightforward because we can use the wonderful <a href=\"https://crates.io/crates/criterion\"><u>criterion</u></a> Rust crate. Criterion provides an api for timing rust code down to the nanosecond by aggregating multiple isolated executions. It also provides feedback on how the performance improves or regresses over time. The input for the benchmark is a large set of synthesized requests with a random number of headers with a uniform distribution of internal vs. non-internal headers. With our tooling and test data we find that our original <code>clear_internal_headers</code> function runs in an average of <b>3.65µs</b>. Now for each new method of clearing headers, we can measure against the same set of requests and get a relative performance difference. </p>\n <div class=\"flex anchor relative\">\n <h2 id=\"reducing-reads\">Reducing Reads</h2>\n <a href=\"#reducing-reads\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>One potentially quick win is to invert how we find the headers that need to be removed from requests. If you look at the original code, you can see that we are evaluating <code>request_header.remove_header(h)</code> for each header in our list of internal headers, so 100+ times. Diagrammatically, it looks like this:</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7y2qHbNfBQeoGRc8PqjBcp/9e8fccb6951a475a26def66695e47635/2442-2.png\" alt=\"2442-2\" class=\"kg-image\" width=\"568\" height=\"244\" loading=\"lazy\"/>\n </figure><p></p><p>Since an average request has significantly fewer than 100 headers (10-30), flipping the lookup direction should reduce the number of reads while yielding the same intersection. Because we are working in Rust (and because <code>retain</code> does not exist for <code>http::HeaderMap</code> <a href=\"https://github.com/hyperium/http/issues/541\"><u>yet</u></a>), we have to collect the identified internal headers in a separate step before removing them from the request. Conceptually, it looks like this:</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6hgLavu1hZbwkw91Tee8e1/4d43b538274ae2c680236ca66791d73b/2442-3.png\" alt=\"2442-3\" class=\"kg-image\" width=\"599\" height=\"351\" loading=\"lazy\"/>\n </figure><p></p><p>Using our benchmarking tool, we can measure the impact of this small change, and surprisingly this is already a substantial improvement. The runtime improves from <b>3.65µs</b> to <b>1.53µs</b>. That’s a 2.39x speed improvement for our function. We can calculate the theoretical CPU percentage by multiplying the starting utilization by the ratio of the new and old times: 1.71% * 1.53 / 3.65 = 0.717%. Unfortunately, if we subtract that from the original 1.71% that only equates to saving 1.71% - 0.717% = <i>0.993%</i> of the total CPU time. We should be able to do better. </p>\n <div class=\"flex anchor relative\">\n <h2 id=\"searching-data-structures\">Searching Data Structures</h2>\n <a href=\"#searching-data-structures\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Now that we have reorganized our function to search a static set of internal headers instead of the actual request, we have the freedom to choose what data structure we store our header name in simply by changing the type of <code>INTERNAL_HEADER_SET</code>.</p>\n <pre class=\"language-RUST\"><code class=\"language-RUST\">pub fn clear_internal_headers(request_header: &amp;mut RequestHeader) {\n let to_remove = request_header\n .headers\n .keys()\n .filter_map(|name| INTERNAL_HEADER_SET.get(name))\n .collect::&lt;Vec&lt;_&gt;&gt;();\n\n\n to_remove.into_iter().for_each(|k| {\n request_header.remove_header(k);\n });</pre></code>\n <p></p><p>Our first attempt used <code>std::HashMap</code>, but there may be other data structures that better suit our needs. All computer science students were taught at some point that hash tables are great because they have constant-time asymptotic behavior, or O(1), for reading. (If you are not familiar with <a href=\"https://www.khanacademy.org/computing/computer-science/algorithms/asymptotic-notation/a/big-o-notation\"><u>big O notation</u></a>, it is a way to express how an algorithm consumes a resource, in this case time, as the input size changes.) This means no matter how large the map gets, reads always take the same amount of time. Too bad this is only partially true. In order to read from a hash table, you have to compute the hash. Computing a hash for strings requires reading every byte, so while read time for a hashmap is constant over the table’s size, it’s linear over key length. So, our goal is to find a data structure that is better than O(L) where L is the length of the key.</p><p>There are a few common data structures that provide for reads that have read behavior that meets our criteria. Sorted sets like <code>BTreeSet</code> use comparisons for searching, and that makes them logarithmic over key length <b>O(log(L))</b>, but they are also logarithmic in size too. The net effect is that even very fast sorted sets like <a href=\"https://crates.io/crates/fst\"><u>FST</u></a> work out to be a little (50 ns) slower in our benchmarks than the standard hashmap.</p><p>State machines like parsers and regex are another common tool for searching for strings, though it’s hard to consider them data structures. These systems work by accepting input one unit at a time and determining on each step whether or not to keep evaluating. Being able to make these determinations at every step means state machines are very fast to identify negative cases (i.e. when a string is not valid or not a match). This is perfect for us because only one or two headers per request on average will be internal. In fact, benchmarking an implementation of <code>clear_internal_headers</code> using regular expressions clocks in as taking about twice as long as the hashmap-based solution. This is impressively fast given that regexes, while powerful, aren&#39;t known for their raw speed. This approach feels promising – we just need something in between a data structure and a state machine. </p><p>That’s where the trie comes in.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"dont-just-trie\">Don’t Just Trie</h2>\n <a href=\"#dont-just-trie\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>A <a href=\"https://en.wikipedia.org/wiki/Trie\"><u>trie</u></a> (pronounced like “try” or “tree”) is a type of <a href=\"https://en.wikipedia.org/wiki/Tree_(data_structure)\"><u>tree data structure</u></a> normally used for prefix searches or auto-complete systems over a known set of strings. The structure of the trie lends itself to this because each node in the trie represents a substring of characters found in the initial set. The connections between the nodes represent the characters that can follow a prefix. Here is a small example of a trie built from the words: “and”, “ant”, “dad”, “do”, &amp; “dot”. </p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5wy48j3XNs9awxRNvjLljC/4e2a05b4e1802eba26f9e10e95bd843f/2442-4.png\" alt=\"2442-4\" class=\"kg-image\" width=\"519\" height=\"291\" loading=\"lazy\"/>\n </figure><p>The root node represents an empty string prefix, so the two lettered edges directed out of it are the only letters that can appear as the first letter in the list of strings, “a” and “d”. Subsequent nodes have increasingly longer prefixes until the final valid words are reached. This layout should make it easy to see how a trie could be useful for quickly identifying strings that are not contained. Even at the root node, we can eliminate any strings that are presented that do not start with “a” or “d”. This paring down of the search space on every step gives reading from a trie the <b>O(log(L))</b> we were looking for … but only for misses. Hits within a trie are still <b>O(L)</b>, but that’s okay, because we are getting misses over 90% of the time.</p><p>Benchmarking a few trie implementations from <a href=\"https://crates.io/search?q=trie\"><u>crates.io</u></a> was disheartening. Remember, most tries are used in response to keyboard events, so optimizing them to run in the hot path of tens of millions of requests per second is not a priority. The fastest existing implementation we found was <a href=\"https://crates.io/crates/radix_trie\"><u>radix_trie</u></a>, but it still clocked in at a full microsecond slower than hashmap. The only thing left to do was write our own implementation of a trie that was optimized for our use case.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"trie-hard\">Trie Hard</h2>\n <a href=\"#trie-hard\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>And we did! Today we are announcing <a href=\"https://github.com/cloudflare/trie-hard\"><u>trie-hard</u></a>. The repository gives a full description of how it works, but the big takeaway is that it gets its speed from storing node relationships in the bits of unsigned integers and keeping the entire tree in a contiguous chunk of memory. In our benchmarks, we found that trie-hard reduced the average runtime for <code>clear_internal_headers</code> to under a microsecond (0.93µs). We can reuse the same formula from above to calculate the expected CPU utilization for trie-hard to be 1.71% * 3.65 / 0.93 = 0.43% That means we have finally achieved and surpassed our goal by reducing the compute utilization of pingora-origin by 1.71% - 0.43% = <b>1.28%</b>! </p><p>Up until now we have been working only in theory and local benchmarking. What really matters is whether our benchmarking reflects real-life behavior. Trie-hard has been running in production since July 2024, and over the course of this project we have been collecting performance metrics from the running production of pingora-origin using a statistical sampling of its stack trace over time. Using this technique, the CPU utilization percentage of a function is estimated by the percent of samples in which the function appears. If we compare the sampled performance of the different versions of <code>clear_internal_headers</code>, we can see that the results from the performance sampling closely match what our benchmarks predicted.</p><table><tr><th><p>Implementation</p></th><th><p>Stack trace samples containing <code>clear_internal_headers</code></p></th><th><p>Actual CPU Usage (%)</p></th><th><p>Predicted CPU Usage (%)</p></th></tr><tr><td><p>Original </p></td><td><p>19 / 1111</p></td><td><p>1.71</p></td><td><p>n/a</p></td></tr><tr><td><p>Hashmap</p></td><td><p>9 / 1103</p></td><td><p>0.82</p></td><td><p>0.72</p></td></tr><tr><td><p>trie-hard</p></td><td><p>4 / 1171</p></td><td><p>0.34</p></td><td><p>0.43</p></td></tr></table>\n <div class=\"flex anchor relative\">\n <h2 id=\"conclusion\">Conclusion</h2>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Optimizing functions and writing new data structures is cool, but the real conclusion for this post is that knowing where your code is slow and by how much is more important than how you go about optimizing it. Take a moment to thank your observability team (if you&#39;re lucky enough to have one), and make use of flame graphs or any other profiling and benchmarking tool you can. Optimizing operations that are already measured in microseconds may seem a little silly, but these small improvements add up.</p>"],"published_at":[0,"2024-09-10T14:00+00:00"],"updated_at":[0,"2024-10-09T23:05:23.370Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5uwVNobeSBws457ad5SoNY/080de413142fc98caffc3c0108912fe4/2442-1-hero.png"],"tags":[1,[[0,{"id":[0,"x8ZMj9p778dTDn2pFIaGP"],"name":[0,"Internet Performance"],"slug":[0,"internet-performance"]}],[0,{"id":[0,"w4e8pkoz9c8xNDVhy9eNe"],"name":[0,"Rust"],"slug":[0,"rust"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"2kPXcZlQ7I9S1hKI5tRYxl"],"name":[0,"Optimization"],"slug":[0,"optimization"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Kevin Guthrie"],"slug":[0,"kevin-guthrie"],"bio":[0],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61WQLEZaZz6iuYFaIyXVqi/ed69550880128eeade8c7d75d97e555f/-W1A6801.jpg"],"location":[0],"website":[0],"twitter":[0,"johnhurt"],"facebook":[0]}]]],"meta_description":[0,"Pingora handles 35M+ requests per second, so saving a few microseconds per request can translate to thousands of dollars saved on computing costs. In this post, we share how we freed up over 500 CPU cores by optimizing one function and announce trie-hard, the open source crate that we created to do it."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"blog-english-only"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/pingora-saving-compute-1-percent-at-a-time"],"metadata":[0,{"title":[0,"A good day to trie-hard: saving compute 1% at a time"],"description":[0,"Pingora handles 35M+ requests per second, so saving a few microseconds per request can translate to thousands of dollars saved on computing costs. In this post, we share how we freed up over 500 CPU cores by optimizing one function and announce trie-hard, the open source crate that we created to do it."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7sPe2ORh9vzsbG6owWUicC/0add1e7522aad2861699ec02672f071a/-2024-_Blog_Template__1200x628_.png"]}]}],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"PostCard","value":true}" await-children=""> <article class="w-50-l mt4 mt2-l mb4 ph3 bb b--gray8 bn-l"> <div class="w-100"> <a href="https://blog-cloudflare-com.translate.goog/pingora-saving-compute-1-percent-at-a-time?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 no-underline gray1" data-testid="post-title"><h2 class="fw5 mt2">A good day to trie-hard: saving compute 1% at a time</h2></a> <p class="f3 fw5 gray5 my" data-testid="post-date">2024-09-10</p> <div class=""> <a href="https://blog-cloudflare-com.translate.goog/tag/internet-performance?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Internet Performance</a><a href="https://blog-cloudflare-com.translate.goog/tag/rust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Rust</a><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Open Source</a><a href="https://blog-cloudflare-com.translate.goog/tag/optimization?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Optimization</a> </div> <p class="f3 fw4 gray1 lh-copy " data-testid="post-content">Pingora handles 35M+ requests per second, so saving a few microseconds per request can translate to thousands of dollars saved on computing costs. In this post, we share how we freed up over 500 CPU cores by optimizing one function and announce trie-hard, the open source crate that we created to do it.<!-- -->...</p> <ul class="author-lists flex pl0"> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/kevin-guthrie?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61WQLEZaZz6iuYFaIyXVqi/ed69550880128eeade8c7d75d97e555f/-W1A6801.jpg" alt="Kevin Guthrie" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/kevin-guthrie?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Kevin Guthrie</a> </div></li> </ul> </div> </article><!--astro:end--> </astro-island><astro-island uid="ZEmetr" prefix="r3" component-url="/_astro/PostCard.DWAKGH4e.js" component-export="PostCard" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"currentPage":[0,1],"isFeaturedImageFirstPost":[0,false],"post":[0,{"id":[0,"1NVmSxeyTXrlaivG80ZNzS"],"title":[0,"Go wild: Wildcard support in Rules and a new open-source wildcard crate"],"slug":[0,"wildcard-rules"],"excerpt":[0,"We’re excited to announce wildcard support across our Ruleset Engine-based products and our open-source wildcard crate in Rust. Configuring rules has never been easier, with powerful pattern matching enabling simple and flexible URL redirects and beyond for users on all plans."],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5SVubtrh9iaqDDQSnA60Me/b511db040441d802b147cb838448ab26/2478-1-hero.png\" alt=\"2478-1-hero\" class=\"kg-image\" width=\"1999\" height=\"1125\" loading=\"lazy\"/>\n </figure><p>Back in 2012, we <a href=\"https://blog.cloudflare.com/introducing-pagerules-fine-grained-feature-co\"><u>introduced</u></a> <a href=\"https://developers.cloudflare.com/rules/page-rules/\"><u>Page Rules</u></a>, a pioneering feature that gave Cloudflare users unprecedented control over how their web traffic was managed. At the time, this was a significant leap forward, enabling users to define <a href=\"https://developers.cloudflare.com/rules/page-rules/reference/wildcard-matching/\"><u>patterns</u></a> for specific URLs and adjust Cloudflare <a href=\"https://developers.cloudflare.com/rules/page-rules/reference/settings/\"><u>features</u></a> on a page-by-page basis. The ability to apply such precise configurations through a simple, user-friendly interface was a major advancement, establishing Page Rules as a cornerstone of our platform.</p><p>Page Rules allowed users to implement a variety of actions, including <a href=\"https://developers.cloudflare.com/rules/url-forwarding/#redirects\"><u>redirects</u></a>, which automatically send visitors from one URL to another. Redirects are crucial for maintaining a seamless user experience on the Internet, whether it&#39;s guiding users <a href=\"https://developers.cloudflare.com/rules/url-forwarding/examples/redirect-new-url/\"><u>from outdated links to new content</u></a> or managing traffic during <a href=\"https://developers.cloudflare.com/rules/url-forwarding/examples/redirect-all-different-hostname/\"><u>site migrations</u></a>.</p><p>As the Internet has evolved, so too have the needs of our users. The demand for greater flexibility, higher performance, and more advanced capabilities led to the development of the <a href=\"https://developers.cloudflare.com/ruleset-engine/\"><u>Ruleset Engine</u></a>, a powerful framework designed to handle complex rule evaluations with unmatched speed and precision.</p><p>In September 2022, we announced and released <a href=\"https://blog.cloudflare.com/dynamic-redirect-rules\"><u>Single Redirects</u></a> as a modern replacement for the <a href=\"https://developers.cloudflare.com/rules/page-rules/how-to/url-forwarding/\"><u>URL Forwarding</u></a> feature of Page Rules. Built on top of the Ruleset Engine, this new product offered a powerful syntax and enhanced performance.</p><p>Despite the <a href=\"https://blog.cloudflare.com/future-of-page-rules\"><u>enhancements</u></a>, one of the most consistent pieces of feedback from our users was the need for wildcard matching and expansion, also known as <a href=\"https://github.com/begin/globbing\"><u>globbing</u></a>. This feature is essential for creating dynamic and flexible URL patterns, allowing users to manage a broader range of scenarios with ease.</p><p>Today we are excited to announce that wildcard support is now available across our Ruleset Engine-based products, including <a href=\"https://developers.cloudflare.com/cache/how-to/cache-rules/\"><u>Cache Rules</u></a>, <a href=\"https://developers.cloudflare.com/rules/compression-rules/\"><u>Compression Rules</u></a>, <a href=\"https://developers.cloudflare.com/rules/configuration-rules/\"><u>Configuration Rules</u></a>, <a href=\"https://developers.cloudflare.com/rules/custom-error-responses/\"><u>Custom Errors</u></a>, <a href=\"https://developers.cloudflare.com/rules/origin-rules/\"><u>Origin Rules</u></a>, <a href=\"https://developers.cloudflare.com/rules/url-forwarding/\"><u>Redirect Rules</u></a>, <a href=\"https://developers.cloudflare.com/rules/snippets/\"><u>Snippets</u></a>, <a href=\"https://developers.cloudflare.com/rules/transform/\"><u>Transform Rules</u></a>, <a href=\"https://developers.cloudflare.com/waf/\"><u>Web Application Firewall (WAF)</u></a>, <a href=\"https://developers.cloudflare.com/waiting-room/\"><u>Waiting Room</u></a>, and more.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"understanding-wildcards\">Understanding wildcards</h3>\n <a href=\"#understanding-wildcards\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Wildcard pattern matching allows users to employ an asterisk <code>(*)</code> in a string to match certain patterns. For example, a single pattern like <code>https://example.com/*/t*st</code> can cover multiple URLs such as <code>https://example.com/en/test</code>, <code>https://example.com/images/toast</code>, and <code>https://example.com/blog/trust</code>.</p><p>Once a segment is captured, it can be used in another expression by referencing the matched wildcard with the <code>${&lt;X&gt;}</code> syntax, where <code>&lt;X&gt;</code> indicates the index of a matched pattern. This is particularly useful in URL forwarding. For instance, the URL pattern <code>https://example.com/*/t*st</code> can redirect to <code>https://${1}.example.com/t${2}st</code>, allowing dynamic and flexible URL redirection. This setup ensures that <code>https://example.com/uk/test</code> is forwarded to <code>https://uk.example.com/test</code>, <code>https://example.com/images/toast</code> to <code>https://images.example.com/toast</code>, and so on.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"challenges-with-single-redirects\">Challenges with Single Redirects</h3>\n <a href=\"#challenges-with-single-redirects\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In Page Rules, redirecting from an old URI path to a new one looked like this:</p><ul><li><p><b>Source URL:</b> <code>https://example.com/old-path/*</code></p></li><li><p><b>Target URL:</b> <code>https://example.com/new-path/$1</code></p></li></ul><p>In comparison, replicating this behaviour in Single Redirects without wildcards required a more complex approach:</p><ul><li><p><b>Filter:</b> <code>(http.host eq &quot;example.com&quot; and starts_with(http.request.uri.path, &quot;/old-path/&quot;))</code></p></li><li><p><b>Expression:</b> <code>concat(&quot;/new-path/&quot;, substring(http.request.uri.path, 10)) (where 10 is the length of /old-path/)</code></p></li></ul><p>This complexity created unnecessary overhead and difficulty, especially for users without access to regular expressions (regex) or the technical expertise to come up with expressions that use nested functions.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"wildcard-support-in-ruleset-engine\">Wildcard support in Ruleset Engine</h3>\n <a href=\"#wildcard-support-in-ruleset-engine\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>With the introduction of wildcard support across our Ruleset Engine-based products, users can now take advantage of the power and flexibility of the Ruleset Engine through simpler and more intuitive configurations. This enhancement ensures high performance while making it easier to create dynamic and flexible URL patterns and beyond.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"whats-new\">What’s new?</h3>\n <a href=\"#whats-new\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <h4>1) Operators &quot;wildcard&quot; and &quot;strict wildcard&quot; in Ruleset Engine:</h4><ul><li><p>&quot;<b>wildcard</b>&quot; (case insensitive): Matches patterns regardless of case (e.g., &quot;test&quot; and &quot;TesT&quot; are treated the same, similar to <a href=\"https://developers.cloudflare.com/rules/page-rules/reference/wildcard-matching/\"><u>Page Rules</u></a>).</p></li><li><p>&quot;<b>strict wildcard</b>&quot; (case sensitive): Matches patterns exactly, respecting case differences (e.g., &quot;test&quot; won&#39;t match &quot;TesT&quot;).</p></li></ul><p>Both operators <a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/operators/#wildcard-matching\"><u>can be applied</u></a> to any string field available in the Ruleset Engine, including full URI, host, headers, cookies, user-agent, country, and more.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/46A6KAfGTCGykWIGiSLItF/c2b0743622244de48369da29fc7c4093/2478-2.png\" alt=\"2478-2\" class=\"kg-image\" width=\"1068\" height=\"632\" loading=\"lazy\"/>\n </figure><p></p><p>This example demonstrates the use of the &quot;wildcard&quot; operator in a <a href=\"https://developers.cloudflare.com/waf/\"><u>Web Application Firewall (WAF)</u></a> rule applied to the User Agent field. This rule matches any incoming request where the User Agent string contains patterns starting with &quot;Mozilla/&quot; and includes specific elements like &quot;Macintosh; Intel Mac OS &quot;, &quot;Gecko/&quot;, and &quot;Firefox/&quot;. Importantly, the wildcard operator is case insensitive, so it captures variations like &quot;mozilla&quot; and &quot;Mozilla&quot; without requiring exact matches.</p><h4>2) Function <code>wildcard_replace()</code> in Single Redirects:</h4><p>In <a href=\"https://developers.cloudflare.com/rules/url-forwarding/single-redirects/\"><u>Single Redirects</u></a>, the <code>wildcard_replace()</code> <a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#wildcard_replace\"><u>function</u></a> allows you to use matched segments in redirect URL targets.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5s2Y9zPgK4AqzD24DNSGU1/e8c456882160ad62b339888d05545f0d/2478-3.png\" alt=\"2478-3\" class=\"kg-image\" width=\"1176\" height=\"726\" loading=\"lazy\"/>\n </figure><p></p><p>Consider the URL pattern <code>https://example.com/*/t*st</code> mentioned earlier. Using <code>wildcard_replace()</code>, you can now set the target URL to <code>https://${1}.example.com/t${2}st</code> and dynamically redirect URLs like <code>https://example.com/uk/test</code> to <code>https://uk.example.com/test</code> and <code>https://example.com/images/toast</code> to <code>https://images.example.com/toast</code>.</p><h4>3) Simplified UI in Single Redirects:</h4><p>We understand that not everyone wants to use advanced Ruleset Engine <a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/functions/\"><u>functions</u></a>, especially for simple URL patterns. That’s why we’ve introduced an easy and intuitive UI for <a href=\"https://developers.cloudflare.com/rules/url-forwarding/single-redirects/\"><u>Single Redirects</u></a> called “wildcard pattern”. This new interface, available under the Rules &gt; Redirect Rules tab of the zone dashboard, lets you specify request and target URL wildcard patterns in seconds without needing to delve into complex functions, much like Page Rules.</p>\n <figure class=\"kg-card kg-image-card\">\n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1y72vKTVFZjDUglnTpC2Nl/3da615997c9e245858356e79dfbbd3ec/2478-4.png\" alt=\"2478-4\" class=\"kg-image\" width=\"1060\" height=\"932\" loading=\"lazy\"/>\n </figure><p></p>\n <div class=\"flex anchor relative\">\n <h3 id=\"how-we-built-it\">How we built it</h3>\n <a href=\"#how-we-built-it\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The Ruleset Engine powering Cloudflare Rules products is written in <a href=\"https://www.rust-lang.org/\"><u>Rust</u></a>. When adding wildcard support, we first explored existing <a href=\"https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html\"><u>Rust crates</u></a> for wildcard matching.</p><p>We considered using the popular <a href=\"https://crates.io/crates/regex\"><code><u>regex</u></code></a> crate, known for its robustness. However, it requires converting wildcard patterns into regular expressions (e.g., <code>*</code> to <code>.*</code>, and <code>?</code> to <code>.</code>) and escaping other characters that are special in regex patterns, which adds complexity.</p><p>We also looked at the <a href=\"https://crates.io/crates/wildmatch\"><code><u>wildmatch</u></code></a> crate, which is designed specifically for wildcard matching and has a couple of advantages over <code>regex</code>. The most obvious one is that there is no need to convert wildcard patterns to regular expressions. More importantly, wildmatch can handle complex patterns efficiently: wildcard matching takes quadratic time – in the worst case the time is proportional to the length of the pattern multiplied by the length of the input string. To be more specific, the time complexity is <i>O(p + ℓ + s ⋅ ℓ)</i>, where <i>p</i> is the length of the wildcard pattern, <i>ℓ</i> the length of the input string, and <i>s</i> the number of asterisk metacharacters in the pattern. (If you are not familiar with <a href=\"https://www.khanacademy.org/computing/computer-science/algorithms/asymptotic-notation/a/big-o-notation\"><u>big O notation</u></a>, it is a way to express how an algorithm consumes a resource, in this case time, as the input size changes.) In the Ruleset Engine, we limit the number of asterisk metacharacters in the pattern to a maximum of 8. This ensures we will have good performance and limits the impact of a bad actor trying to consume too much CPU time by targeting extremely complicated patterns and input strings.</p><p>Unfortunately, <code>wildmatch</code> did not meet all our requirements. Ruleset Engine uses byte-oriented matching, and <code>wildmatch</code> works only on UTF-8 strings. We also have to support escape sequences – for example, you should be able to represent a literal * in the pattern with <code>\\*</code>.</p><p>Last but not least, to implement the <a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#wildcard_replace\"><code><u>wildcard_replace() function</u></code></a> we needed not only to be able to match, but also to be able to replace parts of strings with captured segments. This is necessary to dynamically create HTTP redirects based on the source URL. For example, to redirect a request from <code>https://example.com/*/page/*</code> to <code>https://example.com/products/${1}?page=${2}</code>, you should be able to define the target URL using an expression like this:</p>\n <pre class=\"language-undefined\"><code class=\"language-undefined\">wildcard_replace(\nhttp.request.full_uri, \n&amp;quot;https://example.com/*/page/*&amp;quot;, \n&amp;quot;https://example.com/products/${1}?page=${2}&amp;quot;\n)</pre></code>\n <p></p><p>This means that in order to implement this function in the Ruleset Engine, we also need our wildcard matching implementation to capture the input substrings that match the wildcard’s metacharacters.</p><p>Given these requirements, we decided to build our own wildcard matching crate. The implementation is based on <a href=\"http://dodobyte.com/wildcard.html\"><u>Kurt&#39;s 2016 iterative algorithm</u></a>, with optimizations from <a href=\"http://developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html\"><u>Krauss’ 2014 algorithm</u></a>. (You can find more information about the algorithm <a href=\"https://github.com/cloudflare/wildcard/blob/v0.2.0/src/lib.rs#L555-L569\"><u>here</u></a>). Our implementation supports byte-oriented matching, escape sequences, and capturing matched segments for further processing.</p><p>Cloudflare’s <a href=\"https://crates.io/crates/wildcard\"><code><u>wildcard crate</u></code></a> is now available and is open-source. You can find the source repository <a href=\"https://github.com/cloudflare/wildcard\"><u>here</u></a>. Contributions are welcome!</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"faqs-and-resources\">FAQs and resources</h3>\n <a href=\"#faqs-and-resources\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>For more details on using wildcards in Rules products, please refer to our updated Ruleset Engine documentation:</p><ul><li><p><a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/operators/#wildcard-matching\"><u>Ruleset Engine Operators</u></a></p></li><li><p><a href=\"https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#wildcard_replace\"><u>Ruleset Engine Functions</u></a></p></li></ul><p>We value your feedback and invite you to share your thoughts in our <a href=\"https://community.cloudflare.com/t/wildcard-support-in-ruleset-engine-products/692658\"><u>community forums</u></a>. Your input directly influences our product and design decisions, helping us make Rules products even better.</p><p>Additionally, check out our <a href=\"https://crates.io/crates/wildcard\"><code><u>wildcard crate</u></code></a> implementation and <a href=\"https://github.com/cloudflare/wildcard\"><u>contribute to its development</u></a>.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"conclusion\">Conclusion</h3>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The new wildcard functionality in Rules is available to all plans and is completely free. This feature is rolling out immediately, and no beta access registration required. </p><p>We are thrilled to offer this much-requested feature and look forward to seeing how you leverage wildcards in your Rules configurations. Try it now and experience the enhanced flexibility and performance. Your feedback is invaluable to us, so please let us know in <a href=\"https://community.cloudflare.com/t/wildcard-support-in-ruleset-engine-products/692658\"><u>community</u></a> how this new feature works for you!</p>"],"published_at":[0,"2024-08-22T14:00+00:00"],"updated_at":[0,"2024-10-09T23:05:24.311Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5SVubtrh9iaqDDQSnA60Me/b511db040441d802b147cb838448ab26/2478-1-hero.png"],"tags":[1,[[0,{"id":[0,"3aRZvV7ApVpkYKGhnNQH4w"],"name":[0,"CDN"],"slug":[0,"cdn"]}],[0,{"id":[0,"4Hr4XSACbfTmrYAA7iESxH"],"name":[0,"Edge Rules"],"slug":[0,"edge-rules"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"w4e8pkoz9c8xNDVhy9eNe"],"name":[0,"Rust"],"slug":[0,"rust"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Nikita Cano"],"slug":[0,"nikita"],"bio":[0,"Product Manager, Rules and Insights (Configuration Rules, Compression Rules, Page Rules, Redirect Rules, Origin Rules, Snippets, Trace, Traffic Sequence, Transform Rules, URL Normalization, etc.)"],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6hGB1W3bIJaibV4F7Qe0L8/56f554eeae28828297135a54eacc3065/nikita.jpeg"],"location":[0,"London, United Kingdom"],"website":[0,"https://nikitacano.com"],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Diogo Sousa"],"slug":[0,"diogo-sousa"],"bio":[0],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CIkwYPr6o1fayekQOuYTk/cb2a91fdf53d4a3c68bb53723537e524/_tmp_mini_magick20230901-2-16mh0ld.jpg"],"location":[0],"website":[0],"twitter":[0],"facebook":[0]}]]],"meta_description":[0,"We’re excited to announce wildcard support across our Ruleset Engine-based products and our open-source wildcard crate in Rust. Configuring rules has never been easier, with powerful pattern matching enabling simple and flexible URL redirects and beyond for users on all plans."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"blog-english-only"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/wildcard-rules"],"metadata":[0,{"title":[0],"description":[0],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3jJGB4rvNAUowp0jvP3jIM/28bc0aa9543ce35a135e879a1fca0c9d/2478-OG.png"]}]}],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"PostCard","value":true}" await-children=""> <article class="w-50-l mt4 mt2-l mb4 ph3 bb b--gray8 bn-l"> <div class="w-100"> <a href="https://blog-cloudflare-com.translate.goog/wildcard-rules?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 no-underline gray1" data-testid="post-title"><h2 class="fw5 mt2">Go wild: Wildcard support in Rules and a new open-source wildcard crate</h2></a> <p class="f3 fw5 gray5 my" data-testid="post-date">2024-08-22</p> <div class=""> <a href="https://blog-cloudflare-com.translate.goog/tag/cdn?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">CDN</a><a href="https://blog-cloudflare-com.translate.goog/tag/edge-rules?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Edge Rules</a><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Open Source</a><a href="https://blog-cloudflare-com.translate.goog/tag/rust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Rust</a><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Developers</a> </div> <p class="f3 fw4 gray1 lh-copy " data-testid="post-content">We’re excited to announce wildcard support across our Ruleset Engine-based products and our open-source wildcard crate in Rust. Configuring rules has never been easier, with powerful pattern matching enabling simple and flexible URL redirects and beyond for users on all plans.<!-- -->...</p> <ul class="author-lists flex pl0"> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/nikita?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6hGB1W3bIJaibV4F7Qe0L8/56f554eeae28828297135a54eacc3065/nikita.jpeg" alt="Nikita Cano" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/nikita?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Nikita Cano</a> </div></li> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/diogo-sousa?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CIkwYPr6o1fayekQOuYTk/cb2a91fdf53d4a3c68bb53723537e524/_tmp_mini_magick20230901-2-16mh0ld.jpg" alt="Diogo Sousa" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/diogo-sousa?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Diogo Sousa</a> </div></li> </ul> </div> </article><!--astro:end--> </astro-island><astro-island uid="4bVxz" prefix="r4" component-url="/_astro/PostCard.DWAKGH4e.js" component-export="PostCard" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"currentPage":[0,1],"isFeaturedImageFirstPost":[0,false],"post":[0,{"id":[0,"Mmf9yB6m0SRgCJfyxvYK8"],"title":[0,"Meta Llama 3.1 now available on Workers AI"],"slug":[0,"meta-llama-3-1-available-on-workers-ai"],"excerpt":[0,"Cloudflare is excited to be a launch partner with Meta to introduce Workers AI support for Llama 3.1"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/673JByQ2oFcE0WKQS8vr1w/e0c9b3e4ad0cc101f40d0f42be668a66/image1-19.png\" alt=\"Meta Llama 3.1 now available on Workers AI\" class=\"kg-image\" width=\"1999\" height=\"1125\" loading=\"lazy\"/>\n \n </figure><p>At Cloudflare, we’re big supporters of the open-source community – and that extends to our approach for <a href=\"https://developers.cloudflare.com/workers-ai/\">Workers AI</a> models as well. Our strategy for our Cloudflare AI products is to provide a top-notch developer experience and toolkit that can help people build applications with open-source models.</p><p>We’re excited to be one of Meta’s launch partners to make their newest <a href=\"https://github.com/meta-llama/llama-models/blob/main/models/llama3_1/MODEL_CARD.md\">Llama 3.1 8B model</a> available to all Workers AI users on Day 1. You can run their latest model by simply swapping out your model ID to <code>@cf/meta/llama-3.1-8b-instruct</code> or test out the model on our <a href=\"https://playground.ai.cloudflare.com\">Workers AI Playground</a>. Llama 3.1 8B is free to use on Workers AI until the model graduates out of beta.</p><p>Meta’s Llama collection of models have consistently shown high-quality performance in areas like general knowledge, steerability, math, tool use, and multilingual translation. Workers AI is excited to continue to distribute and serve the Llama collection of models on our serverless inference platform, powered by our globally distributed GPUs.</p><p>The Llama 3.1 model is particularly exciting, as it is released in a higher precision (bfloat16), incorporates function calling, and adds support across 8 languages. Having multilingual support built-in means that you can use Llama 3.1 to write prompts and receive responses directly in languages like English, French, German, Hindi, Italian, Portuguese, Spanish, and Thai. Expanding model understanding to more languages means that your applications have a bigger reach across the world, and it’s all possible with just one model.</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">const answer = await env.AI.run(&#039;@cf/meta/llama-3.1-8b-instruct&#039;, {\n stream: true,\n messages: [{\n &quot;role&quot;: &quot;user&quot;,\n &quot;content&quot;: &quot;Qu&#039;est-ce que ç&#039;est verlan en français?&quot;\n }],\n});</pre></code>\n <p>Llama 3.1 also introduces native function calling (also known as tool calls) which allows LLMs to generate structured JSON outputs which can then be fed into different APIs. This means that function calling is supported out-of-the-box, without the need for a fine-tuned variant of Llama that specializes in tool use. Having this capability built-in means that you can use one model across various tasks.</p><p>Workers AI recently announced <a href=\"/embedded-function-calling\">embedded function calling</a>, which is now usable with Meta Llama 3.1 as well. Our embedded function calling gives developers a way to run their inference tasks far more efficiently than traditional architectures, leveraging Cloudflare Workers to reduce the number of requests that need to be made manually. It also makes use of our open-source <a href=\"https://www.npmjs.com/package/@cloudflare/ai-utils\">ai-utils</a> package, which helps you orchestrate the back-and-forth requests for function calling along with other helper methods that can automatically generate tool schemas. Below is an example function call to Llama 3.1 with embedded function calling that then stores key-values in Workers KV.</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">const response = await runWithTools(env.AI, &quot;@cf/meta/llama-3.1-8b-instruct&quot;, {\n messages: [{ role: &quot;user&quot;, content: &quot;Greet the user and ask them a question&quot; }],\n tools: [{\n name: &quot;Store in memory&quot;,\n description: &quot;Store everything that the user talks about in memory as a key-value pair.&quot;,\n parameters: {\n type: &quot;object&quot;,\n properties: {\n key: {\n type: &quot;string&quot;,\n description: &quot;The key to store the value under.&quot;,\n },\n value: {\n type: &quot;string&quot;,\n description: &quot;The value to store.&quot;,\n },\n },\n required: [&quot;key&quot;, &quot;value&quot;],\n },\n function: async ({ key, value }) =&gt; {\n await env.KV.put(key, value);\n\n return JSON.stringify({\n success: true,\n });\n }\n }]\n})</pre></code>\n <p>We’re excited to see what you build with these new capabilities. As always, use of the new model should be conducted with Meta’s <a href=\"https://github.com/meta-llama/llama-models/blob/main/models/llama3_1/USE_POLICY.md\">Acceptable Use Policy</a> and <a href=\"https://github.com/meta-llama/llama-models/blob/main/models/llama3_1/LICENSE\">License</a> in mind. Take a look at our <a href=\"https://developers.cloudflare.com/workers-ai/models/llama-3.1-8b-instruct/\">developer documentation</a> to get started!</p>"],"published_at":[0,"2024-07-23T16:15:55.000+01:00"],"updated_at":[0,"2024-10-09T23:28:47.900Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7rL1LoWn7GhWtDIPYloJ2J/6e917385456d515289f0993bf40c4a7a/meta-llama-3-1-available-on-workers-ai.png"],"tags":[1,[[0,{"id":[0,"1Wf1Dpb2AFicG44jpRT29y"],"name":[0,"Workers AI"],"slug":[0,"workers-ai"]}],[0,{"id":[0,"6Foe3R8of95cWVnQwe5Toi"],"name":[0,"AI"],"slug":[0,"ai"]}],[0,{"id":[0,"6QktrXeEFcl4e2dZUTZVGl"],"name":[0,"Product News"],"slug":[0,"product-news"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Michelle Chen"],"slug":[0,"michelle"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hrcl3aVtUbBuCMeuXETWy/93dbfbc7d41c09ba35d863312dbde89d/michelle.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@_mchenco"],"facebook":[0,null]}],[0,{"name":[0,"Nikhil Kothari"],"slug":[0,"nikhil"],"bio":[0,"Director, Strategic Partnerships"],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7KZ3JdO5ODe3FLdN8ng1Nc/0dbaf23c66ad71fd5b338587ce057ae2/nikhil.jpeg"],"location":[0,"San Francisco"],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"Cloudflare is excited to be a launch partner with Meta to introduce Workers AI support for Llama 3.1."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Meta Llama 3.1 now available on Workers AI Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/meta-llama-3-1-available-on-workers-ai"],"metadata":[0,{"title":[0,"Meta Llama 3.1 now available on Workers AI"],"description":[0,"Cloudflare is excited to be a launch partner with Meta to introduce Workers AI support for Llama 3.1."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61xrrKiIbJxFIi22jOG9M1/b9d093804d79cb2cbda553b5f90a9568/meta-llama-3-1-available-on-workers-ai-a0EKv0.png"]}]}],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"PostCard","value":true}" await-children=""> <article class="w-50-l mt4 mt2-l mb4 ph3 bb b--gray8 bn-l"> <div class="w-100"> <a href="https://blog-cloudflare-com.translate.goog/meta-llama-3-1-available-on-workers-ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 no-underline gray1" data-testid="post-title"><h2 class="fw5 mt2">Meta Llama 3.1 now available on Workers AI</h2></a> <p class="f3 fw5 gray5 my" data-testid="post-date">2024-07-23</p> <div class=""> <a href="https://blog-cloudflare-com.translate.goog/tag/workers-ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Workers AI</a><a href="https://blog-cloudflare-com.translate.goog/tag/ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">AI</a><a href="https://blog-cloudflare-com.translate.goog/tag/product-news?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Product News</a><a href="https://blog-cloudflare-com.translate.goog/tag/developer-platform?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Developer Platform</a><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Developers</a><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1" data-testid="post-tag">Open Source</a> </div> <p class="f3 fw4 gray1 lh-copy " data-testid="post-content">Cloudflare is excited to be a launch partner with Meta to introduce Workers AI support for Llama 3.1<!-- -->...</p> <ul class="author-lists flex pl0"> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/michelle?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hrcl3aVtUbBuCMeuXETWy/93dbfbc7d41c09ba35d863312dbde89d/michelle.jpg" alt="Michelle Chen" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/michelle?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Michelle Chen</a> </div></li> <li class="list flex items-center pr2 mb3"><a href="https://blog-cloudflare-com.translate.goog/author/nikhil?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7KZ3JdO5ODe3FLdN8ng1Nc/0dbaf23c66ad71fd5b338587ce057ae2/nikhil.jpeg" alt="Nikhil Kothari" width="62" height="62"></a> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/nikhil?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw4 f3 no-underline black">Nikhil Kothari</a> </div></li> </ul> </div> </article><!--astro:end--> </astro-island> <astro-island uid="1558o1" prefix="r5" component-url="/_astro/MorePosts.FeEYGmKG.js" component-export="default" renderer-url="/_astro/client.BQCS8AJJ.js" props="{"locale":[0,"en-us"],"posts":[1,[[0,{"id":[0,"28DvCOWrN5gKA9IBZqrdc3"],"title":[0,"Embedded function calling in Workers AI: easier, smarter, faster"],"slug":[0,"embedded-function-calling"],"excerpt":[0,"Introducing a new way to do function calling in Workers AI by running function code alongside your inference. Plus, a new @cloudflare/ai-utils package to make getting started as simple as possible"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6s64pzMizJT22AKVCpeYu2/1db371bffec9a960c8f59c69330970bb/image2-14.png\" alt=\"Embedded function calling in Workers AI: easier, smarter, faster\" class=\"kg-image\" width=\"1800\" height=\"1013\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h2 id=\"introducing-embedded-function-calling-and-a-new-ai-utils-package\">Introducing embedded function calling and a new ai-utils package</h2>\n <a href=\"#introducing-embedded-function-calling-and-a-new-ai-utils-package\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Today, we’re excited to announce a novel way to do function calling that co-locates LLM inference with function execution, and a new ai-utils package that upgrades the developer experience for function calling.</p><p>This is a follow-up to our <a href=\"https://x.com/CloudflareDev/status/1803849609078284315\">mid-June announcement for traditional function calling</a>, which allows you to leverage a Large Language Model (LLM) to intelligently generate structured outputs and pass them to an API call. Function calling has been largely adopted and standardized in the industry as a way for AI models to help perform actions on behalf of a user.</p><p>Our goal is to make building with AI as easy as possible, which is why we’re introducing a new <a href=\"https://www.npmjs.com/package/@cloudflare/ai-utils\">@cloudflare/ai-utils</a> npm package that allows developers to get started quickly with embedded function calling. These helper tools drastically simplify your workflow by actually executing your function code and dynamically generating tools from OpenAPI specs. We’ve also open-sourced our ai-utils package, which you can find on <a href=\"https://github.com/cloudflare/ai-utils\">GitHub</a>. With both embedded function calling and our ai-utils, you’re one step closer to creating intelligent AI agents, and from there, the possibilities are endless.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"why-cloudflares-ai-platform\">Why Cloudflare’s AI platform?</h2>\n <a href=\"#why-cloudflares-ai-platform\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>OpenAI has been the gold standard when it comes to having performant model inference and a great developer experience. However, they mostly support their closed-source models, while we want to also promote the open-source ecosystem of models. One of our goals with Workers AI is to match the developer experience you might get from OpenAI, but with open-source models.</p><p>There are other open-source inference providers out there like <a href=\"https://azure.microsoft.com/en-us/solutions/ai\">Azure</a> or <a href=\"https://aws.amazon.com/bedrock/\">Bedrock</a>, but most of them are focused on serving inference and the underlying infrastructure, rather than being a developer toolkit. While there are external libraries and frameworks like AI SDK that help developers build quickly with simple abstractions, they rely on upstream providers to do the actual inference. With <a href=\"https://developers.cloudflare.com/workers-ai/\">Workers AI</a>, it’s the best of both worlds – we offer open-source model inference and a killer developer experience out of the box.</p><p>With the release of embedded function calling and ai-utils today, we’ve advanced how we do inference for function calling and improved the developer experience by making it dead simple for developers to start building AI experiences.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-does-traditional-function-calling-work\">How does traditional function calling work?</h2>\n <a href=\"#how-does-traditional-function-calling-work\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Traditional LLM function calling allows customers to specify a set of function names and required arguments along with a prompt when running inference on an LLM. The LLM returns the names and arguments for the functions that the customer can then make to perform actions. These actions give LLMs the ability to do things like fetch fresh data not present in the training dataset and &quot;perform actions&quot; based on user intent.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2vZ1PHwC5e6RUkxQRKfBW0/1812907a22283b5eef02e92c747e8a73/image3-15.png\" alt=\"\" class=\"kg-image\" width=\"1786\" height=\"990\" loading=\"lazy\"/>\n \n </figure><p>Traditional function calling requires multiple back-and-forth requests passing through the network in order to get to the final output. This includes requests to your origin server, an inference provider, and external APIs. As a developer, you have to orchestrate all the back-and-forths and handle all the requests and responses. If you were building complex agents with multi-tool calls or recursive tool calls, it gets infinitely harder. Fortunately, this doesn’t have to be the case, and we’ve solved it for you.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"embedded-function-calling\">Embedded function calling</h2>\n <a href=\"#embedded-function-calling\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>With Workers AI, our inference runtime is the Workers platform, and the Workers platform can be seen as a global compute network of distributed functions (RPCs). With this model, we can run inference using Workers AI, and supply not only the function names and arguments, but also the runtime function code to be executed. Rather than performing multiple round-trips across networks, the LLM inference and function can run in the same execution environment, cutting out all the unnecessary requests.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15yHmiG9OmRj9KkuLHteRm/2c50ad90b4736fbc7496a8495d58e1fe/image1-23.png\" alt=\"\" class=\"kg-image\" width=\"1996\" height=\"690\" loading=\"lazy\"/>\n \n </figure><p>Cloudflare is one of the few inference providers that is able to do this because we offer more than just inference – our developer platform has compute, storage, inference, and more, all within the same Workers runtime.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"we-made-it-easy-for-you-with-a-new-ai-utils-package\">We made it easy for you with a new ai-utils package</h3>\n <a href=\"#we-made-it-easy-for-you-with-a-new-ai-utils-package\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>And to make it as simple as possible, we created a <a href=\"https://www.npmjs.com/package/@cloudflare/ai-utils\"><code>@cloudflare/ai-utils</code></a> package that you can use to get started. These powerful abstractions cut down on the logic you have to implement to do function calling – it just works out of the box.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"runwithtools\">runWithTools</h3>\n <a href=\"#runwithtools\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p><code>runWithTool</code>s is our method that you use to do embedded function calling. You pass in your AI binding (env.AI), model, prompt messages, and tools. The tools array includes the description of the function, similar to traditional function calling, but you also pass in the function code that needs to be executed. This method makes the inference calls and executes the function code in one single step. <code>runWithTools</code> is also able to handle multiple function calls, recursive tool calls, validation for model responses, streaming for the final response, and other features.</p><p>Another feature to call out is a helper method called <code>autoTrimTools</code> that automatically selects the relevant tools and trims the tools array based on the names and descriptions. We do this by adding an initial LLM inference call to intelligently trim the tools array before the actual function-calling inference call is made. We found that <code>autoTrimTools</code> helped decrease the number of total tokens used in the entire process (especially when there’s a large number of tools provided) because there’s significantly fewer input tokens used when generating the arguments list. You can choose to use <code>autoTrimTools</code> by setting it as a parameter in the <code>runWithTools</code> method.</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">const response = await runWithTools(env.AI,&quot;@hf/nousresearch/hermes-2-pro-mistral-7b&quot;,\n {\n messages: [{ role: &quot;user&quot;, content: &quot;What&#039;s the weather in Austin, Texas?&quot;}],\n tools: [\n {\n name: &quot;getWeather&quot;,\n description: &quot;Return the weather for a latitude and longitude&quot;,\n parameters: {\n type: &quot;object&quot;,\n properties: {\n latitude: {\n type: &quot;string&quot;,\n description: &quot;The latitude for the given location&quot;\n },\n longitude: {\n type: &quot;string&quot;,\n description: &quot;The longitude for the given location&quot;\n }\n },\n required: [&quot;latitude&quot;, &quot;longitude&quot;]\n },\n\t // function code to be executed after tool call\n function: async ({ latitude, longitude }) =&gt; {\n const url = `https://api.weatherapi.com/v1/current.json?key=${env.WEATHERAPI_TOKEN}&amp;q=${latitude},${longitude}`\n const res = await fetch(url).then((res) =&gt; res.json())\n\n return JSON.stringify(res)\n }\n }\n ]\n },\n {\n streamFinalResponse: true,\n maxRecursiveToolRuns: 5,\n trimFunction: autoTrimTools,\n verbose: true,\n strictValidation: true\n }\n)</pre></code>\n \n <div class=\"flex anchor relative\">\n <h3 id=\"createtoolsfromopenapispec\">createToolsFromOpenAPISpec</h3>\n <a href=\"#createtoolsfromopenapispec\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>For many use cases, users will need to make a request to an external API call during function calling to get the output needed. Instead of having to hardcode the exact API endpoints in your tools array, we made a helper function that takes in an OpenAPI spec and dynamically generates the corresponding tool schemas and API endpoints you’ll need for the function call. You call <code>createToolsFromOpenAPISpec</code> from within runWithTools and it’ll dynamically populate everything for you.</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">const response = await runWithTools(env.AI, &quot;@hf/nousresearch/hermes-2-pro-mistral-7b&quot;, {\n messages: [{ role: &quot;user&quot;,content: &quot;Can you name me 5 repos created by Cloudflare&quot;}],\n tools: [\n ...(await createToolsFromOpenAPISpec( &quot;https://raw.githubusercontent.com/github/rest-api-description/main/descriptions-next/api.github.com/api.github.com.json&quot;\n ))\n ]\n})</pre></code>\n \n <div class=\"flex anchor relative\">\n <h2 id=\"putting-it-all-together\">Putting it all together</h2>\n <a href=\"#putting-it-all-together\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>When you make a function calling inference request with <code>runWithTools</code> and <code>createToolsFromOpenAPISpec</code>, the only thing you need is the prompts – the rest is automatically handled. The LLM will choose the correct tool based on the prompt, the runtime will execute the function needed, and you’ll get a fast, intelligent response from the model. By leveraging our Workers runtime’s bindings and RPC calls along with our global network, we can execute everything from a single location close to the user, enabling developers to easily write complex agentic chains with fewer lines of code.</p><p>We’re super excited to help people build intelligent AI systems with our new embedded function calling and powerful tools. Check out our <a href=\"https://developers.cloudflare.com/workers-ai/function-calling/\">developer docs</a> on how to get started, and let us know what you think on <a href=\"https://discord.cloudflare.com\">Discord</a>.</p>"],"published_at":[0,"2024-06-27T18:00:09.000+01:00"],"updated_at":[0,"2024-10-09T23:28:32.524Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5fJ5fF4luhUlJEOsRKqc8X/0b307037c96cd66d10efd4a49d98b93b/embedded-function-calling.png"],"tags":[1,[[0,{"id":[0,"6QktrXeEFcl4e2dZUTZVGl"],"name":[0,"Product News"],"slug":[0,"product-news"]}],[0,{"id":[0,"1Wf1Dpb2AFicG44jpRT29y"],"name":[0,"Workers AI"],"slug":[0,"workers-ai"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"6Foe3R8of95cWVnQwe5Toi"],"name":[0,"AI"],"slug":[0,"ai"]}],[0,{"id":[0,"3vILtHLIQU7d8BaWzXAPv4"],"name":[0,"Internship Experience"],"slug":[0,"internship-experience"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Harley Turan"],"slug":[0,"harley"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ozodx2P7ECyPDX81E74NC/9757235a7313dcc5f6f559bb47c16620/harley.JPG"],"location":[0,null],"website":[0,"https://hturan.com"],"twitter":[0,"@hturan"],"facebook":[0,null]}],[0,{"name":[0,"Dhravya Shah"],"slug":[0,"dhravya"],"bio":[0,"18, passionate dev who ships (a lot) Building a better (intern)et at Cloudflare AI. building Supermemory.ai. 2x acquired founder. reader, guitarist, space enthusiast"],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1xcx1Lxlh4a2yXgBcYSRtH/65fbda8d9808f1aa44367e666b159a06/dhravya.jpg"],"location":[0,"Austin, Texas"],"website":[0,"https://dhravya.dev"],"twitter":[0,"@dhravyashah"],"facebook":[0,null]}],[0,{"name":[0,"Michelle Chen"],"slug":[0,"michelle"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hrcl3aVtUbBuCMeuXETWy/93dbfbc7d41c09ba35d863312dbde89d/michelle.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@_mchenco"],"facebook":[0,null]}]]],"meta_description":[0,"Introducing a new way to do function calling in Workers AI by running function code alongside your inference. Plus, a new @cloudflare/ai-utils package to make getting started as simple as possible. "],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Embedded function calling in Workers AI: easier, smarter, faster Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/embedded-function-calling"],"metadata":[0,{"title":[0,"Embedded function calling in Workers AI: easier, smarter, faster"],"description":[0,"Introducing a new way to do function calling in Workers AI by running function code alongside your inference. Plus, a new @cloudflare/ai-utils package to make getting started as simple as possible. "],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4tVuF3kgHm6aJj0RaSFG6T/5949d6172c8429fede2ab1e9d66c9001/embedded-function-calling-w4f2f7.png"]}]}],[0,{"id":[0,"3EErej51Xbc8xOYpGL8ggy"],"title":[0,"AI Gateway is generally available: a unified interface for managing and scaling your generative AI workloads"],"slug":[0,"ai-gateway-is-generally-available"],"excerpt":[0,"AI Gateway is an AI ops platform that provides speed, reliability, and observability for your AI applications. With a single line of code, you can unlock powerful features including rate limiting, custom caching, real-time logs, and aggregated analytics across multiple providers"],"featured":[0,false],"html":[0,"<p></p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5GsB2wwIevC3G2m0PGOAhz/d9eaeea0933d269b39fcda70c22881b7/image4-3.png\" alt=\"AI Gateway is generally available: a unified interface for managing and scaling your generative AI workloads.\" class=\"kg-image\" width=\"1200\" height=\"675\" loading=\"lazy\"/>\n \n </figure><p>During Developer Week in April 2024, we announced General Availability of <a href=\"/workers-ai-ga-huggingface-loras-python-support\">Workers AI</a>, and today, we are excited to announce that AI Gateway is Generally Available as well. Since its launch to beta <a href=\"/announcing-ai-gateway\">in September 2023 during Birthday Week</a>, we’ve proxied over 500 million requests and are now prepared for you to use it in production.</p><p>AI Gateway is an AI ops platform that offers a unified interface for managing and scaling your generative AI workloads. At its core, it acts as a proxy between your service and your inference provider(s), regardless of where your model runs. With a single line of code, you can unlock a set of powerful features focused on performance, security, reliability, and observability – think of it as your <a href=\"https://www.cloudflare.com/learning/network-layer/what-is-the-control-plane/\">control plane</a> for your AI ops. And this is just the beginning – we have a roadmap full of exciting features planned for the near future, making AI Gateway the tool for any organization looking to get more out of their AI workloads.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6M6hDWXdRH2rZETQK4UlPe/444269e8d23056252e9e17aa08cef333/image6-1.png\" alt=\"architecture diagram illustrating the setup of AI Gateway as a forward proxy\" class=\"kg-image\" width=\"1200\" height=\"380\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h2 id=\"why-add-a-proxy-and-why-cloudflare\">Why add a proxy and why Cloudflare?</h2>\n <a href=\"#why-add-a-proxy-and-why-cloudflare\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The AI space moves fast, and it seems like every day there is a new model, provider, or framework. Given this high rate of change, it’s hard to keep track, especially if you’re using more than one model or provider. And that’s one of the driving factors behind launching AI Gateway – we want to provide you with a single consistent control plane for all your models and tools, even if they change tomorrow, and then again the day after that.</p><p>We&#39;ve talked to a lot of developers and organizations building AI applications, and one thing is clear: they want more observability, control, and tooling around their AI ops. This is something many of the AI providers are lacking as they are deeply focused on model development and less so on platform features.</p><p>Why choose Cloudflare for your AI Gateway? Well, in some ways, it feels like a natural fit. We&#39;ve spent the last 10+ years helping build a better Internet by running one of the largest global networks, helping customers around the world with performance, reliability, and security – Cloudflare is used as a <a href=\"https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/\">reverse proxy</a> by nearly 20% of all websites. With our expertise, it felt like a natural progression – change one line of code, and we can help with observability, reliability, and control for your AI applications – all in one control plane – so that you can get back to building.</p><p>Here is that one line code change using the OpenAI JS SDK. And check out <a href=\"https://developers.cloudflare.com/ai-gateway/providers/\">our docs</a> to reference other providers, SDKs, and languages.</p>\n <pre class=\"language-js\"><code class=\"language-js\">import OpenAI from &#039;openai&#039;;\n\nconst openai = new OpenAI({\napiKey: &#039;my api key&#039;, // defaults to process.env[&quot;OPENAI_API_KEY&quot;]\n\tbaseURL: &quot;https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_slug}/openai&quot;\n});</pre></code>\n <!--kg-card-begin: html--><p></p><!--kg-card-end: html-->\n <div class=\"flex anchor relative\">\n <h2 id=\"whats-included-today\">What’s included today?</h2>\n <a href=\"#whats-included-today\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>After talking to customers, it was clear that we needed to focus on some foundational features before moving onto some of the more advanced ones. While we&#39;re really excited about what’s to come, here are the key features available in GA today:</p><p><b>Analytics</b>: Aggregate metrics from across multiple providers. See traffic patterns and usage including the number of requests, tokens, and costs over time.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3gFXixQSV6rVUM9V6ew1W4/db974469f45415b7ae0f0af45c30e7f3/pasted-image-0--10-.png\" alt=\"\" class=\"kg-image\" width=\"1328\" height=\"1216\" loading=\"lazy\"/>\n \n </figure><p><b>Real-time logs:</b> Gain insight into requests and errors as you build.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/31KebDSmQfi9lW87mh3oZy/541a90575637dc860e1ef28972958ed4/image8-1.png\" alt=\"Real-time logs from running three requests to Cohere\" class=\"kg-image\" width=\"810\" height=\"212\" loading=\"lazy\"/>\n \n </figure><p><b>Caching:</b> Enable custom caching rules and use Cloudflare’s cache for repeat requests instead of hitting the original model provider API, helping you save on cost and latency.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2bZw1HaJUP48B3MbXiATpx/0e7ee230a8b1c62e782efd466177fb5f/image1-10.png\" alt=\"Set up rules for caching requests\" class=\"kg-image\" width=\"1999\" height=\"389\" loading=\"lazy\"/>\n \n </figure><p><b>Rate limiting:</b> Control how your application scales by limiting the number of requests your application receives to control costs or prevent abuse.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4icXzN7Z8VuZw17KdzKl2X/60466c7cbe3869c14aa7a7ad90c40159/image5-9.png\" alt=\"Set up rules for rate limiting traffic\" class=\"kg-image\" width=\"1999\" height=\"385\" loading=\"lazy\"/>\n \n </figure><p><b>Support for your favorite providers:</b> AI Gateway now natively supports Workers AI plus 10 of the most popular providers, including <a href=\"https://x.com/CloudflareDev/status/1791204770394648901\">Groq and Cohere</a> as of mid-May 2024.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ORhtmLzCTOKLVrCyhyEZK/53be2a20c4d6bd7dd3cdcd2657ef6455/image2-10.png\" alt=\"Real time logs from three LLM providers - Cohere, Groq, and OpenAI\" class=\"kg-image\" width=\"1134\" height=\"932\" loading=\"lazy\"/>\n \n </figure><p><b>Universal endpoint:</b> In case of errors, improve resilience by defining <a href=\"https://developers.cloudflare.com/ai-gateway/configuration/fallbacks/\">request fallbacks</a> to another model or inference provider.</p>\n <pre class=\"language-js\"><code class=\"language-js\">curl https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_slug} -X POST \\\n --header &#039;Content-Type: application/json&#039; \\\n --data &#039;[\n {\n &quot;provider&quot;: &quot;workers-ai&quot;,\n &quot;endpoint&quot;: &quot;@cf/meta/llama-2-7b-chat-int8&quot;,\n &quot;headers&quot;: {\n &quot;Authorization&quot;: &quot;Bearer {cloudflare_token}&quot;,\n &quot;Content-Type&quot;: &quot;application/json&quot;\n },\n &quot;query&quot;: {\n &quot;messages&quot;: [\n {\n &quot;role&quot;: &quot;system&quot;,\n &quot;content&quot;: &quot;You are a friendly assistant&quot;\n },\n {\n &quot;role&quot;: &quot;user&quot;,\n &quot;content&quot;: &quot;What is Cloudflare?&quot;\n }\n ]\n }\n },\n {\n &quot;provider&quot;: &quot;openai&quot;,\n &quot;endpoint&quot;: &quot;chat/completions&quot;,\n &quot;headers&quot;: {\n &quot;Authorization&quot;: &quot;Bearer {open_ai_token}&quot;,\n &quot;Content-Type&quot;: &quot;application/json&quot;\n },\n &quot;query&quot;: {\n &quot;model&quot;: &quot;gpt-3.5-turbo&quot;,\n &quot;stream&quot;: true,\n &quot;messages&quot;: [\n {\n &quot;role&quot;: &quot;user&quot;,\n &quot;content&quot;: &quot;What is Cloudflare?&quot;\n }\n ]\n }\n }\n]&#039;</pre></code>\n <!--kg-card-begin: html--><p></p><!--kg-card-end: html-->\n <div class=\"flex anchor relative\">\n <h2 id=\"whats-coming-up\">What’s coming up?</h2>\n <a href=\"#whats-coming-up\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We&#39;ve gotten a lot of feedback from developers, and there are some obvious things on the horizon such as persistent logs and custom metadata – foundational features that will help unlock the real magic down the road.</p><p>But let&#39;s take a step back for a moment and share our vision. At Cloudflare, we believe our platform is much more powerful as a unified whole than as a collection of individual parts. This mindset applied to our AI products means that they should be easy to use, combine, and run in harmony.</p><p>Let&#39;s imagine the following journey. You initially onboard onto Workers AI to run inference with the latest open source models. Next, you enable AI Gateway to gain better visibility and control, and start storing persistent logs. Then you want to start tuning your inference results, so you leverage your persistent logs, our prompt management tools, and our built in eval functionality. Now you&#39;re making analytical decisions to improve your inference results. With each data driven improvement, you want more. So you implement our feedback API which helps annotate inputs/outputs, in essence building a structured data set. At this point, you are one step away from a one-click fine tune that can be deployed instantly to our global network, and it doesn&#39;t stop there. As you continue to collect logs and feedback, you can continuously rebuild your fine tune adapters in order to deliver the best results to your end users.</p><p>This is all just an aspirational story at this point, but this is how we envision the future of AI Gateway and our AI suite as a whole. You should be able to start with the most basic setup and gradually progress into more advanced workflows, all without leaving <a href=\"https://www.cloudflare.com/ai-solution/\">Cloudflare’s AI platform</a>. In the end, it might not look exactly as described above, but you can be sure that we are committed to providing the best AI ops tools to help make Cloudflare the best place for AI.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-do-i-get-started\">How do I get started?</h2>\n <a href=\"#how-do-i-get-started\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>AI Gateway is available to use today on all plans. If you haven’t yet used AI Gateway, check out our <a href=\"https://developers.cloudflare.com/ai-gateway/\">developer documentation</a> and get started now. AI Gateway’s core features available today are offered for free, and all it takes is a Cloudflare account and one line of code to get started. In the future, more premium features, such as persistent logging and secrets management will be available subject to fees. If you have any questions, reach out on our <a href=\"http://discord.cloudflare.com\">Discord channel</a>.</p>"],"published_at":[0,"2024-05-22T14:00:17.000+01:00"],"updated_at":[0,"2024-10-10T00:21:59.074Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6dIZ63ejPd9T3lR66GvbGk/b475e0d94aa62b1394b0046f5c386647/ai-gateway-is-generally-available.png"],"tags":[1,[[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"1Wf1Dpb2AFicG44jpRT29y"],"name":[0,"Workers AI"],"slug":[0,"workers-ai"]}],[0,{"id":[0,"5OywGP63AdM9Umyvaku8OP"],"name":[0,"Connectivity Cloud"],"slug":[0,"connectivity-cloud"]}],[0,{"id":[0,"1GyUhE8o287lrdNSpdRUIe"],"name":[0,"AI Gateway"],"slug":[0,"ai-gateway"]}],[0,{"id":[0,"6Foe3R8of95cWVnQwe5Toi"],"name":[0,"AI"],"slug":[0,"ai"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Kathy Liao"],"slug":[0,"kathy"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2XeJHmfHmhCUmRwC7aeCWR/fb2194fd1e4bed0667242d081354f5f2/kathy.png"],"location":[0,"Seattle"],"website":[0,null],"twitter":[0,"@kathyyliao"],"facebook":[0,null]}],[0,{"name":[0,"Michelle Chen"],"slug":[0,"michelle"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hrcl3aVtUbBuCMeuXETWy/93dbfbc7d41c09ba35d863312dbde89d/michelle.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@_mchenco"],"facebook":[0,null]}],[0,{"name":[0,"Phil Wittig"],"slug":[0,"phil"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2FbDE6kgoEtV8l8hu6W85e/f31d42ea6b3cf65cfb08fb9fca5d0010/phil.jpeg"],"location":[0,null],"website":[0,null],"twitter":[0,"@pdwittig"],"facebook":[0,null]}]]],"meta_description":[0,null],"primary_author":[0,{}],"localeList":[0,{"name":[0,"AI Gateway is generally available: a unified interface for managing and scaling your generative AI workloads Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"Translated for Locale"],"frFR":[0,"Translated for Locale"],"deDE":[0,"Translated for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"Translated for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"Translated for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/ai-gateway-is-generally-available"],"metadata":[0,{"title":[0,"AI Gateway is generally available: a unified interface for managing and scaling your generative AI workloads"],"description":[0,null],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1EWXM9QqB7UbqLFl1AtujG/3cec4ff7139977cf14349d91cff78278/ai-gateway-is-generally-available-BHx2Qt.png"]}]}],[0,{"id":[0,"38GzyqaZUYTF8fJFAiwpfx"],"title":[0,"Open sourcing Pingora: our Rust framework for building programmable network services"],"slug":[0,"pingora-open-source"],"excerpt":[0,"Pingora, our framework for building programmable and memory-safe network services, is now open source. Get started using Pingora today"],"featured":[0,false],"html":[0,"<p></p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7aQBSJRQlM3b1ZRdvJycqY/72f9bd908abc139faba41716074d69d5/Rock-crab-pingora-open-source-mascot.png\" alt=\"Open sourcing Pingora: our Rust framework for building programmable network services\" class=\"kg-image\" width=\"2000\" height=\"548\" loading=\"lazy\"/>\n \n </figure><p>Today, we are proud to open source Pingora, the Rust framework we have been using to build services that power a significant portion of the traffic on Cloudflare. Pingora is <a href=\"https://github.com/cloudflare/pingora\">released</a> under the <a href=\"https://www.apache.org/licenses/LICENSE-2.0\">Apache License version 2.0</a>.</p><p>As mentioned in our previous blog post, <a href=\"/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet\">Pingora</a> is a Rust async multithreaded framework that assists us in constructing HTTP proxy services. Since our last blog post, Pingora has handled nearly a quadrillion Internet requests across our global network.</p><p>We are open sourcing Pingora to help build a better and more secure Internet beyond our own infrastructure. We want to provide tools, ideas, and inspiration to our customers, users, and others to build their own Internet infrastructure using a memory safe framework. Having such a framework is especially crucial given the increasing awareness of the importance of memory safety across the <a href=\"https://www.theregister.com/2022/09/20/rust_microsoft_c/\">industry</a> and the <a href=\"https://www.whitehouse.gov/oncd/briefing-room/2024/02/26/press-release-technical-report/\">US government</a>. Under this common goal, we are collaborating with the <a href=\"https://www.abetterinternet.org/\">Internet Security Research Group</a> (ISRG) <a href=\"https://www.memorysafety.org/blog/introducing-river\">Prossimo project</a> to help advance the adoption of Pingora in the Internet’s most critical infrastructure.</p><p>In our <a href=\"/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet\">previous blog post</a>, we discussed why and how we built Pingora. In this one, we will talk about why and how you might use Pingora.</p><p>Pingora provides building blocks for not only proxies but also clients and servers. Along with these components, we also provide a few utility libraries that implement common logic such as <a href=\"/how-pingora-keeps-count/\">event counting</a>, error handling, and caching.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"whats-in-the-box\">What’s in the box</h3>\n <a href=\"#whats-in-the-box\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Pingora provides libraries and APIs to build services on top of HTTP/1 and HTTP/2, TLS, or just TCP/UDS. As a proxy, it supports HTTP/1 and HTTP/2 end-to-end, gRPC, and websocket proxying. (HTTP/3 support is on the roadmap.) It also comes with customizable load balancing and failover strategies. For compliance and security, it supports both the commonly used OpenSSL and BoringSSL libraries, which come with FIPS compliance and <a href=\"https://pq.cloudflareresearch.com/\">post-quantum crypto</a>.</p><p>Besides providing these features, Pingora provides filters and callbacks to allow its users to fully customize how the service should process, transform and forward the requests. These APIs will be especially familiar to OpenResty and NGINX users, as many map intuitively onto OpenResty&#39;s &quot;*_by_lua&quot; callbacks.</p><p>Operationally, Pingora provides zero downtime graceful restarts to upgrade itself without dropping a single incoming request. Syslog, Prometheus, Sentry, OpenTelemetry and other must-have observability tools are also easily integrated with Pingora as well.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"who-can-benefit-from-pingora\">Who can benefit from Pingora</h3>\n <a href=\"#who-can-benefit-from-pingora\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>You should consider Pingora if:</p><p><b>Security is your top priority:</b> Pingora is a more memory safe alternative for services that are written in C/C++. While some might argue about memory safety among programming languages, from our practical experience, we find ourselves way less likely to make coding mistakes that lead to memory safety issues. Besides, as we spend less time struggling with these issues, we are more productive implementing new features.</p><p><b>Your service is performance-sensitive:</b> Pingora is fast and efficient. As explained in our previous blog post, we saved a lot of CPU and memory resources thanks to Pingora’s multi-threaded architecture. The saving in time and resources could be compelling for workloads that are sensitive to the cost and/or the speed of the system.</p><p><b>Your service requires extensive customization:</b> The APIs that the Pingora proxy framework provides are highly programmable. For users who wish to build a customized and advanced gateway or load balancer, Pingora provides powerful yet simple ways to implement it. We provide examples in the next section.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"lets-build-a-load-balancer\">Let’s build a load balancer</h2>\n <a href=\"#lets-build-a-load-balancer\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Let&#39;s explore Pingora&#39;s programmable API by building a simple load balancer. The load balancer will select between <a href=\"https://1.1.1.1/\">https://1.1.1.1/</a> and <a href=\"https://1.0.0.1/\">https://1.0.0.1/</a> to be the upstream in a round-robin fashion.</p><p>First let’s create a blank HTTP proxy.</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">pub struct LB();\n\n#[async_trait]\nimpl ProxyHttp for LB {\n async fn upstream_peer(...) -&gt; Result&lt;Box&lt;HttpPeer&gt;&gt; {\n todo!()\n }\n}</pre></code>\n <p>Any object that implements the <code>ProxyHttp</code> trait (similar to the concept of an interface in C++ or Java) is an HTTP proxy. The only required method there is <code>upstream_peer()</code>, which is called for every request. This function should return an <code>HttpPeer</code> which contains the origin IP to connect to and how to connect to it.</p><p>Next let’s implement the round-robin selection. The Pingora framework already provides the <code>LoadBalancer</code> with common selection algorithms such as round robin and hashing, so let’s just use it. If the use case requires more sophisticated or customized server selection logic, users can simply implement it themselves in this function.</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">pub struct LB(Arc&lt;LoadBalancer&lt;RoundRobin&gt;&gt;);\n\n#[async_trait]\nimpl ProxyHttp for LB {\n async fn upstream_peer(...) -&gt; Result&lt;Box&lt;HttpPeer&gt;&gt; {\n let upstream = self.0\n .select(b&quot;&quot;, 256) // hash doesn&#039;t matter for round robin\n .unwrap();\n\n // Set SNI to one.one.one.one\n let peer = Box::new(HttpPeer::new(upstream, true, &quot;one.one.one.one&quot;.to_string()));\n Ok(peer)\n }\n}</pre></code>\n <p>Since we are connecting to an HTTPS server, the SNI also needs to be set. Certificates, timeouts, and other connection options can also be set here in the HttpPeer object if needed.</p><p>Finally, let&#39;s put the service in action. In this example we hardcode the origin server IPs. In real life workloads, the origin server IPs can also be discovered dynamically when the <code>upstream_peer()</code> is called or in the background. After the service is created, we just tell the LB service to listen to 127.0.0.1:6188. In the end we created a Pingora server, and the server will be the process which runs the load balancing service.</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">fn main() {\n let mut upstreams = LoadBalancer::try_from_iter([&quot;1.1.1.1:443&quot;, &quot;1.0.0.1:443&quot;]).unwrap();\n\n let mut lb = pingora_proxy::http_proxy_service(&amp;my_server.configuration, LB(upstreams));\n lb.add_tcp(&quot;127.0.0.1:6188&quot;);\n\n let mut my_server = Server::new(None).unwrap();\n my_server.add_service(lb);\n my_server.run_forever();\n}</pre></code>\n <p>Let’s try it out:</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">curl 127.0.0.1:6188 -svo /dev/null\n&gt; GET / HTTP/1.1\n&gt; Host: 127.0.0.1:6188\n&gt; User-Agent: curl/7.88.1\n&gt; Accept: */*\n&gt; \n&lt; HTTP/1.1 403 Forbidden</pre></code>\n <p>We can see that the proxy is working, but the origin server rejects us with a 403. This is because our service simply proxies the Host header, 127.0.0.1:6188, set by curl, which upsets the origin server. How do we make the proxy correct that? This can simply be done by adding another filter called <code>upstream_request_filter</code>. This filter runs on every request after the origin server is connected and before any HTTP request is sent. We can add, remove or change http request headers in this filter.</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">async fn upstream_request_filter(…, upstream_request: &amp;mut RequestHeader, …) -&gt; Result&lt;()&gt; {\n upstream_request.insert_header(&quot;Host&quot;, &quot;one.one.one.one&quot;)\n}</pre></code>\n <p>Let’s try again:</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">curl 127.0.0.1:6188 -svo /dev/null\n&lt; HTTP/1.1 200 OK</pre></code>\n <p>This time it works! The complete example can be found <a href=\"https://github.com/cloudflare/pingora/blob/main/pingora-proxy/examples/load_balancer.rs\">here</a>.</p><p>Below is a very simple diagram of how this request flows through the callback and filter we used in this example. The Pingora proxy framework currently provides <a href=\"https://github.com/cloudflare/pingora/blob/main/docs/user_guide/phase.md\">more filters</a> and callbacks at different stages of a request to allow users to modify, reject, route and/or log the request (and response).</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/WvHfhONcYEEL4kPRwGzKp/f2456d177e727063a49265eea831b8af/Flow_Diagram.png\" alt=\"\" class=\"kg-image\" width=\"710\" height=\"213\" loading=\"lazy\"/>\n \n </figure><p>Behind the scenes, the Pingora proxy framework takes care of connection pooling, TLS handshakes, reading, writing, parsing requests and any other common proxy tasks so that users can focus on logic that matters to them.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"open-source-present-and-future\">Open source, present and future</h2>\n <a href=\"#open-source-present-and-future\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Pingora is a library and toolset, not an executable binary. In other words, Pingora is the engine that powers a car, not the car itself. Although Pingora is production-ready for industry use, we understand a lot of folks want a batteries-included, ready-to-go web service with low or no-code config options. Building that application on top of Pingora will be the focus of our collaboration with the ISRG to expand Pingora&#39;s reach. Stay tuned for future announcements on that project.</p><p>Other caveats to keep in mind:</p><ul><li><p><b>Today, API stability is not guaranteed.</b> Although we will try to minimize how often we make breaking changes, we still reserve the right to add, remove, or change components such as request and response filters as the library evolves, especially during this pre-1.0 period.</p></li><li><p><b>Support for non-Unix based operating systems is not currently on the roadmap.</b> We have no immediate plans to support these systems, though this could change in the future.</p></li></ul>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-to-contribute\">How to contribute</h2>\n <a href=\"#how-to-contribute\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Feel free to raise bug reports, documentation issues, or feature requests in our GitHub <a href=\"https://github.com/cloudflare/pingora/issues\">issue tracker</a>. Before opening a pull request, we strongly suggest you take a look at our <a href=\"https://github.com/cloudflare/pingora/blob/main/.github/CONTRIBUTING.md\">contribution guide</a>.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"conclusion\">Conclusion</h2>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In this blog we announced the open source of our Pingora framework. We showed that Internet entities and infrastructure can benefit from Pingora’s security, performance and customizability. We also demonstrated how easy it is to use Pingora and how customizable it is.</p><p>Whether you&#39;re building production web services or experimenting with network technologies we hope you find value in Pingora. It&#39;s been a long journey, but sharing this project with the open source community has been a goal from the start. We&#39;d like to thank the Rust community as Pingora is built with many great open-sourced Rust crates. Moving to a memory safe Internet may feel like an impossible journey, but it&#39;s one we hope you join us on.</p>"],"published_at":[0,"2024-02-28T15:00:11.000+00:00"],"updated_at":[0,"2024-10-10T00:22:15.287Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/f24aeLVQUjlbXRxDzdWm9/dc66227c33b56143feba15c9f3da26c9/pingora-open-source.png"],"tags":[1,[[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"w4e8pkoz9c8xNDVhy9eNe"],"name":[0,"Rust"],"slug":[0,"rust"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"4gN0ARax0fHxjtZL07THOe"],"name":[0,"Performance"],"slug":[0,"performance"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Yuchen Wu"],"slug":[0,"yuchen"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Ku4RmxaCrMYgBRauBkpSY/6053cf7e36a88fa5f0ca956780a12863/yuchen.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Edward Wang"],"slug":[0,"edward-h-wang"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3SR2WJJhMUA6NjeEtB1Z2A/7bf4f81bf09f441fbccc5cfb19c39710/edward-h-wang.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}],[0,{"name":[0,"Andrew Hauck"],"slug":[0,"andrew-hauck"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1crH945j3ZNGgaRazYIlca/4df0c031df672eed876bbe3e167b4597/andrew-hauck.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,null],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Open sourcing Pingora: our Rust framework for building programmable network services Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"Translated for Locale"],"frFR":[0,"Translated for Locale"],"deDE":[0,"Translated for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"Translated for Locale"],"ptBR":[0,"Translated for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"Translated for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/pingora-open-source"],"metadata":[0,{"title":[0,"Open sourcing Pingora: our Rust framework for building programmable network services"],"description":[0,null],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7gb9sRwlJjetA6l01T4XgO/0f49172d6d83749d6c45e729d05d95e3/pingora-open-source-H9vTzf.png"]}]}],[0,{"id":[0,"58duMHip3s7DGNRo47fweV"],"title":[0,"Adding new LLMs, text classification and code generation models to the Workers AI catalog"],"slug":[0,"february-2024-workersai-catalog-update"],"excerpt":[0,"Workers AI is now bigger and better with 8 new models and improved model performance"],"featured":[0,false],"html":[0,"<p></p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5bUPtXgJU5a797f2LMkI7k/b830752f7f52d7ab46bf396e00c93ea7/image2-1.png\" alt=\"Adding new LLMs, text classification and code generation models to the Workers AI catalog\" class=\"kg-image\" width=\"1800\" height=\"1013\" loading=\"lazy\"/>\n \n </figure><p>Over the last few months, the Workers AI team has been hard at work making improvements to our AI platform. We launched back in September, and in November, we added more models like Code Llama, Stable Diffusion, Mistral, as well as improvements like streaming and longer context windows.</p><p>Today, we’re excited to announce the release of eight new models.</p><p>The new models are highlighted below, but check out our full model catalog with over 20 models <a href=\"https://developers.cloudflare.com/workers-ai/\">in our developer docs.</a></p><p><b>Text generation</b>@hf/thebloke/llama-2-13b-chat-awq@hf/thebloke/zephyr-7b-beta-awq@hf/thebloke/mistral-7b-instruct-v0.1-awq@hf/thebloke/openhermes-2.5-mistral-7b-awq@hf/thebloke/neural-chat-7b-v3-1-awq@hf/thebloke/llamaguard-7b-awq</p><p><b>Code generation</b>@hf/thebloke/deepseek-coder-6.7b-base-awq@hf/thebloke/deepseek-coder-6.7b-instruct-awq</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/693UrRZZSZJo8omR5ex2Vk/ac76b952bac6fd613cb8e2c79b7e2f10/image1.png\" alt=\"\" class=\"kg-image\" width=\"1999\" height=\"988\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h3 id=\"bringing-you-the-best-of-open-source\">Bringing you the best of open source</h3>\n <a href=\"#bringing-you-the-best-of-open-source\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Our mission is to support a wide array of open source models and tasks. In line with this, we&#39;re excited to announce a preview of the latest models and features available for deployment on Cloudflare&#39;s network.</p><p>One of the standout models is <code>deep-seek-coder-6.7b</code>, which notably scores <a href=\"https://github.com/deepseek-ai/deepseek-coder\">approximately 15% higher</a> on popular benchmarks against comparable Code Llama models. This performance advantage is attributed to its diverse training data, which includes both English and Chinese code generation datasets. In addition, the <code>openhermes-2.5-mistral-7b</code> model showcases how high quality fine-tuning datasets can improve the accuracy of base models. This Mistral 7b fine-tune outperforms the base model by <a href=\"https://huggingface.co/teknium/OpenHermes-2.5-Mistral-7B#benchmark-results\">approximately 10% on many LLM benchmarks</a>.</p><p>We&#39;re also introducing innovative models that incorporate Activation-aware Weight Quantization (AWQ), such as the <code>llama-2-13b-awq</code>. This quantization technique is just one of the strategies to improve memory efficiency in Large Language Models. While quantization generally boosts inference efficiency in AI models, it often does so at the expense of precision. AWQ strikes a balance to mitigate this tradeoff.</p><p>The pace of progress in AI can be overwhelming, but Cloudflare&#39;s Workers AI simplifies getting started with the latest models. We handle the latest advancements and make them easily accessible from a Worker or our HTTP APIs. You are only ever an API call or Workers binding away from cutting-edge models. Simply put, Workers AI allows developers to concentrate on delivering exceptional user experiences without the burdens of deployment, infrastructure, or scalability concerns.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"using-ai-to-safeguard-your-applications\">Using AI to safeguard your applications</h3>\n <a href=\"#using-ai-to-safeguard-your-applications\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>A newer use case we support with this model catalog update is the ability to classify and check prompts and responses with <code>llamaguard-7b-awq</code>. We’ve all heard of stories of AI bots that unintentionally give answers that might be inappropriate or harmful. This <a href=\"https://ai.meta.com/research/publications/llama-guard-llm-based-input-output-safeguard-for-human-ai-conversations/\">new LlamaGuard mode</a>l allows developers to detect and categorize when a prompt or a response is potentially unsafe, allowing them to control the inputs and outputs of their applications with developer-defined categories.</p><p>Here’s an example of an application that takes a user input and compares that to an example set of defined unsafe categories to determine if the input is safe or not. You can customize the content categories depending on your use case. <i>The below example categories are provided for informational purposes only, and Cloudflare does not make any guarantees regarding the performance of Llama Guard or any outcomes or results.</i></p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">import { Ai } from &#039;@cloudflare/ai&#039;;\n\nexport default {\n\tasync fetch(request, env) {\n\t\tconst payload = await request.json();\n\t\t// Modify these to your needs, this is the example from Meta\n\t\tconst unsafeCategories = `\n01: Violence and Hate.\nShould not\n- Help people plan or engage in violence.\n- Encourage people to engage in violence.\n- Express hateful, derogatory, or demeaning sentiments against people based on sensitive personal characteristics like their race, color, religion, national origin, sexual orientation, gender, gender identity, or disability.\n- Encourage discrimination against people based on those characteristics and should not use slurs or create insults based on those characteristics.\nCan\n- Provide information on violence and discrimination.\n- Discuss the topics of hate and violence, and can discuss historical events involving violence.\n02: Sexual Content.\nShould not\n- Engage in sexually explicit (i.e., erotic) conversations and should not encourage people to engage in sexual activities.\nCan\n- Discuss topics related to sexuality and sexual education.\n- Provide general information regarding sexual activities.\n- Provide guidance on topics related to sexual health.\n03: Criminal Planning.\nShould not\n- Help people plan or engage in criminal activities like theft, kidnapping, or financial crimes, especially when the person indicates that they may act on the information provided to them.\n- Encourage people to engage in criminal activities or condone criminal behavior.\nCan\n- Help people understand how crimes are committed, provided that the response is not so detailed that it could be used to provide a roadmap for future crimes.\n- Help people understand how systems work that are used to prevent crimes, provided those descriptions do not provide detailed information on how to defeat those systems.\n04: Guns and Illegal Weapons.\nShould not\n- Assist in the planning or execution of firearm or illegal weapons crimes, especially when the person indicates that they may act on the information provided to them.\n- Encourage people to engage in firearm or illegal weapons crimes or condone such behavior.\nCan\n- Discuss firearms and the arguments for and against firearm ownership.\n05: Regulated or Controlled Substances.\nShould not\n- Encourage or assist people in the trafficking, consumption, or use of regulated or controlled substances, including but not limited to illegal drugs, tobacco, alcohol, and cannabis.\n- Assist or encourage people to create such substances when it is illegal to do so.\nCan\n- Discuss the use of regulated or controlled substances, and can provide general information on those substances (ex: what their chemical composition is and what their effects are).\n- Provide information on how those substances are manufactured, provided that the information is not so detailed as to provide step-by-step instructions for their creation.\n06: Self-Harm.\nShould not\n- Encourage people to harm themselves.\n- Romanticize or condone self-harm or suicide.\n- Provide information regarding the methods of suicide or self-harm.\n- Provide any information or advice, other than information regarding appropriate health resources, to people who express or imply that they wish to harm themselves.\nCan\n- Provide general information regarding self-harm and suicide (ex: statistics, the mental health challenges faced by those who take these actions).\nShould\n- Provide information regarding appropriate health resources to people who imply or express the desire to harm themselves.\n\t\t`;\n\n\t\t// Assemble the prompt\n\t\tconst prompt = `Task: Check if there is unsafe content in &#039;User&#039; messages in conversations according to our safety policy with the below categories.\n\n\t\t&lt;BEGIN UNSAFE CONTENT CATEGORIES&gt;\n\t\t${unsafeCategories}\n\t\t&lt;END UNSAFE CONTENT CATEGORIES&gt;\n\n\t\t&lt;BEGIN CONVERSATION&gt;\n\t\tUser: ${payload.userContent}\n\t\t&lt;END CONVERSATION&gt;\n\t`;\n\t\tconst ai = new Ai(env.AI);\n\t\tconst response = await ai.run(&#039;@hf/thebloke/llamaguard-7b-awq&#039;, {\n\t\t\tprompt,\n\t\t});\n\t\treturn Response.json(response);\n\t},\n};</pre></code>\n \n <div class=\"flex anchor relative\">\n <h3 id=\"how-do-i-get-started\">How do I get started?</h3>\n <a href=\"#how-do-i-get-started\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Try out our new models within the AI section of the <a href=\"https://dash.cloudflare.com/?to=/:account/ai/workers-ai\">Cloudflare dashboard</a> or take a look at our <a href=\"https://developers.cloudflare.com/workers-ai/models/\">Developer Docs</a> to get started. With the Workers AI platform you can build an app with Workers and Pages, store data with R2, D1, Workers KV, or Vectorize, and run model inference with Workers AI – all in one place. Having more models allows developers to build all different kinds of applications, and we plan to continually update our model catalog to bring you the best of open-source.</p><p>We’re excited to see what you build! If you’re looking for inspiration, take a look at our <a href=\"https://workers.cloudflare.com/built-with/collections/ai-workers/\">collection of “Built-with” stories</a> that highlight what others are building on Cloudflare’s Developer Platform. Stay tuned for a pricing announcement and higher usage limits coming in the next few weeks, as well as more models coming soon. <a href=\"https://discord.cloudflare.com/\">Join us on Discord</a> to share what you’re working on and any feedback you might have.</p>"],"published_at":[0,"2024-02-06T20:00:10.000+00:00"],"updated_at":[0,"2024-10-09T23:05:55.731Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3S4G7LAYYULwVHAC0tT5Nd/054eab507026b4b8218bbe40c4a9bf5c/february-2024-workersai-catalog-update.png"],"tags":[1,[[0,{"id":[0,"1Wf1Dpb2AFicG44jpRT29y"],"name":[0,"Workers AI"],"slug":[0,"workers-ai"]}],[0,{"id":[0,"6hbkItfupogJP3aRDAq6v8"],"name":[0,"Cloudflare Workers"],"slug":[0,"workers"]}],[0,{"id":[0,"6Foe3R8of95cWVnQwe5Toi"],"name":[0,"AI"],"slug":[0,"ai"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Michelle Chen"],"slug":[0,"michelle"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hrcl3aVtUbBuCMeuXETWy/93dbfbc7d41c09ba35d863312dbde89d/michelle.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@_mchenco"],"facebook":[0,null]}],[0,{"name":[0,"Logan Grasby"],"slug":[0,"logan"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5869GeKNHcGlAW7UZoOwY0/e5f207789cbf7de875fcdf9848b1b450/logan.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@LoganGrasby"],"facebook":[0,null]}]]],"meta_description":[0,"Workers AI is now bigger and better with 8 new models and improved model performance."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Adding new LLMs, text classification and code generation models to the Workers AI catalog Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"Translated for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"Translated for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/february-2024-workersai-catalog-update"],"metadata":[0,{"title":[0,"Adding new LLMs, text classification and code generation models to the Workers AI catalog"],"description":[0,"Workers AI is now bigger and better with 8 new models and improved model performance."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7BxMJBTlS1zIYa7RauuHIf/63666fe4132dcb6c89343949e8d6d04d/february-2024-workersai-catalog-update-heB9xy.png"]}]}],[0,{"id":[0,"5R4Wv17SBVwevN7lzcL2GW"],"title":[0,"Introducing Foundations - our open source Rust service foundation library"],"slug":[0,"introducing-foundations-our-open-source-rust-service-foundation-library"],"excerpt":[0,"Foundations is a foundational Rust library, designed to help scale programs for distributed, production-grade systems"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2yQdeNHftkPvZAq7tcEYN9/eaf572b5329ea631b4df66b36e14e538/image1-4.png\" alt=\"Introducing Foundations - our open source Rust service foundation library\" class=\"kg-image\" width=\"1999\" height=\"1125\" loading=\"lazy\"/>\n \n </figure><p>In this blog post, we&#39;re excited to present Foundations, our foundational library for Rust services, now released as <a href=\"https://github.com/cloudflare/foundations\">open source on GitHub</a>. Foundations is a foundational Rust library, designed to help scale programs for distributed, production-grade systems. It enables engineers to concentrate on the core business logic of their services, rather than the intricacies of production operation setups.</p><p>Originally developed as part of our <a href=\"/introducing-oxy/\">Oxy proxy framework</a>, Foundations has evolved to serve a wider range of applications. For those interested in exploring its technical capabilities, we recommend consulting the library’s <a href=\"https://docs.rs/foundations/latest/foundations/\">API documentation</a>. Additionally, this post will cover the motivations behind Foundations&#39; creation and provide a concise summary of its key features. Stay with us to learn more about how Foundations can support your Rust projects.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"what-is-foundations\">What is Foundations?</h2>\n <a href=\"#what-is-foundations\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In software development, seemingly minor tasks can become complex when scaled up. This complexity is particularly evident when comparing the deployment of services on server hardware globally to running a program on a personal laptop.</p><p>The key question is: what fundamentally changes when transitioning from a simple laptop-based prototype to a full-fledged service in a production environment? Through our experience in developing numerous services, we&#39;ve identified several critical differences:</p><ul><li><p><b>Observability</b>: locally, developers have access to various tools for monitoring and debugging. However, these tools are not as accessible or practical when dealing with thousands of software instances running on remote servers.</p></li><li><p><b>Configuration</b>: local prototypes often use basic, sometimes hardcoded, configurations. This approach is impractical in production, where changes require a more flexible and dynamic configuration system. Hardcoded settings are cumbersome, and command-line options, while common, don&#39;t always suit complex hierarchical configurations or align with the &quot;Configuration as Code&quot; paradigm.</p></li><li><p><b>Security</b>: services in production face a myriad of security challenges, exposed to diverse threats from external sources. Basic security hardening becomes a necessity.</p></li></ul><p>Addressing these distinctions, Foundations emerges as a comprehensive library, offering solutions to these challenges. Derived from our Oxy proxy framework, Foundations brings the tried-and-tested functionality of Oxy to a broader range of Rust-based applications at Cloudflare.</p><p>Foundations was developed with these guiding principles:</p><ul><li><p><b>High modularity</b>: recognizing that many services predate Foundations, we designed it to be modular. Teams can adopt individual components at their own pace, facilitating a smooth transition.</p></li><li><p><b>API ergonomics</b>: a top priority for us is user-friendly library interaction. Foundations leverages Rust&#39;s procedural macros to offer an intuitive, well-documented API, aiming for minimal friction in usage.</p></li><li><p><b>Simplified setup and configuration</b>: our goal is for engineers to spend minimal time on setup. Foundations is designed to be &#39;plug and play&#39;, with essential functions working immediately and adjustable settings for fine-tuning. We understand that this focus on ease of setup over extreme flexibility might be debatable, as it implies a trade-off. Unlike other libraries that cater to a wide range of environments with potentially verbose setup requirements, Foundations is tailored for specific, production-tested environments and workflows. This doesn&#39;t restrict Foundations’ adaptability to other settings, but we approach this with compile-time features to manage setup workflows, rather than a complex setup API.</p></li></ul><p>Next, let&#39;s delve into the components Foundations offers. To better illustrate the functionality that Foundations provides we will refer to the <a href=\"https://github.com/cloudflare/foundations/tree/main/examples/http_server\">example web server</a> from Foundations’ source code repository.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"telemetry\">Telemetry</h3>\n <a href=\"#telemetry\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In any production system, observability, which we refer to as telemetry, plays an essential role. Generally, three primary types of telemetry are adequate for most service needs:</p><ul><li><p><b>Logging</b>: this involves recording arbitrary textual information, which can be enhanced with tags or structured fields. It&#39;s particularly useful for documenting operational errors that aren&#39;t critical to the service.</p></li><li><p><b>Tracing</b>: this method offers a detailed timing breakdown of various service components. It&#39;s invaluable for identifying performance bottlenecks and investigating issues related to timing.</p></li><li><p><b>Metrics</b>: these are quantitative data points about the service, crucial for monitoring the overall health and performance of the system.</p></li></ul><p>Foundations integrates an API that encompasses all these telemetry aspects, consolidating them into a unified package for ease of use.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"tracing\">Tracing</h3>\n <a href=\"#tracing\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Foundations’ tracing API shares similarities with <a href=\"https://github.com/tokio-rs/tracing\">tokio/tracing</a>, employing a comparable approach with implicit context propagation, instrumentation macros, and futures wrapping:</p>\n <pre class=\"language-rust\"><code class=\"language-rust\">#[tracing::span_fn(&quot;respond to request&quot;)]\nasync fn respond(\n endpoint_name: Arc&lt;String&gt;,\n req: Request&lt;Body&gt;,\n routes: Arc&lt;Map&lt;String, ResponseSettings&gt;&gt;,\n) -&gt; Result&lt;Response&lt;Body&gt;, Infallible&gt; {\n …\n}</pre></code>\n <p>Refer to the <a href=\"https://github.com/cloudflare/foundations/blob/347548000cab0ac549f8f23e2a0ce9e1147b7640/examples/http_server/main.rs#L154\">example web server</a> and <a href=\"https://docs.rs/foundations/latest/foundations/telemetry/tracing/index.html\">documentation</a> for more comprehensive examples.</p><p>However, Foundations distinguishes itself in a few key ways:</p><ul><li><p><b>Simplified API</b>: we&#39;ve streamlined the setup process for tracing, aiming for a more minimalistic approach compared to tokio/tracing.</p></li><li><p><b>Enhanced trace sampling flexibility</b>: Foundations allows for selective override of the sampling ratio in specific code branches. This feature is particularly useful for detailed performance bug investigations, enabling a balance between global trace sampling for overall <a href=\"https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/\">performance monitoring</a> and targeted sampling for specific accounts, connections, or requests.</p></li><li><p><b>Distributed trace stitching</b>: our API supports the integration of trace data from multiple services, contributing to a comprehensive view of the entire pipeline. This functionality includes fine-tuned control over sampling ratios, allowing upstream services to dictate the sampling of specific traffic flows in downstream services.</p></li><li><p><b>Trace forking capability</b>: addressing the challenge of long-lasting connections with numerous multiplexed requests, Foundations introduces trace forking. This feature enables each request within a connection to have its own trace, linked to the parent connection trace. This method significantly simplifies the analysis and improves performance, particularly for connections handling thousands of requests.</p></li></ul><p>We regard telemetry as a vital component of our software, not merely an optional add-on. As such, we believe in rigorous testing of this feature, considering it our primary tool for monitoring software operations. Consequently, Foundations includes an API and user-friendly macros to facilitate the collection and analysis of tracing data within tests, presenting it in a format conducive to assertions.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"logging\">Logging</h3>\n <a href=\"#logging\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Foundations’ logging API shares its foundation with tokio/tracing and <a href=\"https://github.com/slog-rs/slog\">slog</a>, but introduces several notable enhancements.</p><p>During our work on various services, we recognized the hierarchical nature of logging contextual information. For instance, in a scenario involving a connection, we might want to tag each log record with the connection ID and HTTP protocol version. Additionally, for requests served over this connection, it would be useful to attach the request URL to each log record, while still including connection-specific information.</p><p>Typically, achieving this would involve creating a new logger for each request, copying tags from the connection’s logger, and then manually passing this new logger throughout the relevant code. This method, however, is cumbersome, requiring explicit handling and storage of the logger object.</p><p>To streamline this process and prevent telemetry from obstructing business logic, we adopted a technique similar to tokio/tracing&#39;s approach for tracing, applying it to logging. This method relies on future instrumentation machinery (<a href=\"https://docs.rs/tracing/latest/tracing/struct.Span.html#in-asynchronous-code\">tracing-rs documentation</a> has a good explanation of the concept), allowing for implicit passing of the current logger. This enables us to &quot;fork&quot; logs for each request and use this forked log seamlessly within the current code scope, automatically propagating it down the call stack, including through asynchronous function calls:</p>\n <pre class=\"language-rust\"><code class=\"language-rust\"> let conn_tele_ctx = TelemetryContext::current();\n\n let on_request = service_fn({\n let endpoint_name = Arc::clone(&amp;endpoint_name);\n\n move |req| {\n let routes = Arc::clone(&amp;routes);\n let endpoint_name = Arc::clone(&amp;endpoint_name);\n\n // Each request gets independent log inherited from the connection log and separate\n // trace linked to the connection trace.\n conn_tele_ctx\n .with_forked_log()\n .with_forked_trace(&quot;request&quot;)\n .apply(async move { respond(endpoint_name, req, routes).await })\n }\n});</pre></code>\n <p>Refer to <a href=\"https://github.com/cloudflare/foundations/blob/347548000cab0ac549f8f23e2a0ce9e1147b7640/examples/http_server/main.rs#L155-L198\">example web server</a> and <a href=\"https://docs.rs/foundations/latest/foundations/telemetry/log/index.html\">documentation</a> for more comprehensive examples.</p><p>In an effort to simplify the user experience, we merged all APIs related to context management into a single, implicitly available in each code scope, TelemetryContext object. This integration not only simplifies the process but also lays the groundwork for future advanced features. These features could blend tracing and logging information into a cohesive narrative by cross-referencing each other.</p><p>Like tracing, Foundations also offers a user-friendly API for testing service’s logging.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"metrics\">Metrics</h3>\n <a href=\"#metrics\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Foundations incorporates the official <a href=\"https://github.com/prometheus/client_rust\">Prometheus Rust client library</a> for its metrics functionality, with a few enhancements for ease of use. One key addition is a procedural macro provided by Foundations, which simplifies the definition of new metrics with typed labels, reducing boilerplate code:</p>\n <pre class=\"language-rust\"><code class=\"language-rust\">use foundations::telemetry::metrics::{metrics, Counter, Gauge};\nuse std::sync::Arc;\n\n#[metrics]\npub(crate) mod http_server {\n /// Number of active client connections.\n pub fn active_connections(endpoint_name: &amp;Arc&lt;String&gt;) -&gt; Gauge;\n\n /// Number of failed client connections.\n pub fn failed_connections_total(endpoint_name: &amp;Arc&lt;String&gt;) -&gt; Counter;\n\n /// Number of HTTP requests.\n pub fn requests_total(endpoint_name: &amp;Arc&lt;String&gt;) -&gt; Counter;\n\n /// Number of failed requests.\n pub fn requests_failed_total(endpoint_name: &amp;Arc&lt;String&gt;, status_code: u16) -&gt; Counter;\n}</pre></code>\n <p>Refer to the <a href=\"https://github.com/cloudflare/foundations/blob/347548000cab0ac549f8f23e2a0ce9e1147b7640/examples/http_server/metrics.rs\">example web server</a> and <a href=\"https://docs.rs/foundations/latest/foundations/telemetry/metrics/index.html\">documentation</a> for more information of how metrics can be defined and used.</p><p>In addition to this, we have refined the approach to metrics collection and structuring. Foundations offers a streamlined, user-friendly API for both these tasks, focusing on simplicity and minimalism.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"memory-profiling\">Memory profiling</h3>\n <a href=\"#memory-profiling\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Recognizing the <a href=\"https://mjeanroy.dev/2021/04/19/Java-in-K8s-how-weve-reduced-memory-usage-without-changing-any-code.html\">efficiency</a> of <a href=\"https://jemalloc.net/\">jemalloc</a> for long-lived services, Foundations includes a feature for enabling jemalloc memory allocation. A notable aspect of jemalloc is its memory profiling capability. Foundations packages this functionality into a straightforward and safe Rust API, making it accessible and easy to integrate.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"telemetry-server\">Telemetry server</h3>\n <a href=\"#telemetry-server\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Foundations comes equipped with a built-in, customizable telemetry server endpoint. This server automatically handles a range of functions including health checks, metric collection, and memory profiling requests.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"security\">Security</h2>\n <a href=\"#security\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>A vital component of Foundations is its robust and ergonomic API for <a href=\"https://en.wikipedia.org/wiki/Seccomp\">seccomp</a>, a Linux kernel feature for syscall sandboxing. This feature enables the setting up of hooks for syscalls used by an application, allowing actions like blocking or logging. Seccomp acts as a formidable line of defense, offering an additional layer of security against threats like arbitrary code execution.</p><p>Foundations provides a simple way to define lists of all allowed syscalls, also allowing a composition of multiple lists (in addition, Foundations ships predefined lists for common use cases):</p>\n <pre class=\"language-rust\"><code class=\"language-rust\"> use foundations::security::common_syscall_allow_lists::{ASYNC, NET_SOCKET_API, SERVICE_BASICS};\n use foundations::security::{allow_list, enable_syscall_sandboxing, ViolationAction};\n\n allow_list! {\n static ALLOWED = [\n ..SERVICE_BASICS,\n ..ASYNC,\n ..NET_SOCKET_API\n ]\n }\n\n enable_syscall_sandboxing(ViolationAction::KillProcess, &amp;ALLOWED)\n </pre></code>\n <p>Refer to the <a href=\"https://github.com/cloudflare/foundations/blob/347548000cab0ac549f8f23e2a0ce9e1147b7640/examples/http_server/main.rs#L239-L254\">web server example</a> and <a href=\"https://docs.rs/foundations/latest/foundations/security/index.html\">documentation</a> for more comprehensive examples of this functionality.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"settings-and-cli\">Settings and CLI</h2>\n <a href=\"#settings-and-cli\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Foundations simplifies the management of service settings and command-line argument parsing. Services built on Foundations typically use YAML files for configuration. We advocate for a design where every service comes with a default configuration that&#39;s functional right off the bat. This philosophy is embedded in Foundations’ settings functionality.</p><p>In practice, applications define their settings and defaults using Rust structures and enums. Foundations then transforms Rust documentation comments into configuration annotations. This integration allows the CLI interface to generate a default, fully annotated YAML configuration files. As a result, service users can quickly and easily understand the service settings:</p>\n <pre class=\"language-rust\"><code class=\"language-rust\">use foundations::settings::collections::Map;\nuse foundations::settings::net::SocketAddr;\nuse foundations::settings::settings;\nuse foundations::telemetry::settings::TelemetrySettings;\n\n#[settings]\npub(crate) struct HttpServerSettings {\n /// Telemetry settings.\n pub(crate) telemetry: TelemetrySettings,\n /// HTTP endpoints configuration.\n #[serde(default = &quot;HttpServerSettings::default_endpoints&quot;)]\n pub(crate) endpoints: Map&lt;String, EndpointSettings&gt;,\n}\n\nimpl HttpServerSettings {\n fn default_endpoints() -&gt; Map&lt;String, EndpointSettings&gt; {\n let mut endpoint = EndpointSettings::default();\n\n endpoint.routes.insert(\n &quot;/hello&quot;.into(),\n ResponseSettings {\n status_code: 200,\n response: &quot;World&quot;.into(),\n },\n );\n\n endpoint.routes.insert(\n &quot;/foo&quot;.into(),\n ResponseSettings {\n status_code: 403,\n response: &quot;bar&quot;.into(),\n },\n );\n\n [(&quot;Example endpoint&quot;.into(), endpoint)]\n .into_iter()\n .collect()\n }\n}\n\n#[settings]\npub(crate) struct EndpointSettings {\n /// Address of the endpoint.\n pub(crate) addr: SocketAddr,\n /// Endoint&#039;s URL path routes.\n pub(crate) routes: Map&lt;String, ResponseSettings&gt;,\n}\n\n#[settings]\npub(crate) struct ResponseSettings {\n /// Status code of the route&#039;s response.\n pub(crate) status_code: u16,\n /// Content of the route&#039;s response.\n pub(crate) response: String,\n}</pre></code>\n <p>The settings definition above automatically generates the following default configuration YAML file:</p>\n <pre class=\"language-yaml\"><code class=\"language-yaml\">---\n# Telemetry settings.\ntelemetry:\n # Distributed tracing settings\n tracing:\n # Enables tracing.\n enabled: true\n # The address of the Jaeger Thrift (UDP) agent.\n jaeger_tracing_server_addr: &quot;127.0.0.1:6831&quot;\n # Overrides the bind address for the reporter API.\n # By default, the reporter API is only exposed on the loopback\n # interface. This won&#039;t work in environments where the\n # Jaeger agent is on another host (for example, Docker).\n # Must have the same address family as `jaeger_tracing_server_addr`.\n jaeger_reporter_bind_addr: ~\n # Sampling ratio.\n #\n # This can be any fractional value between `0.0` and `1.0`.\n # Where `1.0` means &quot;sample everything&quot;, and `0.0` means &quot;don&#039;t sample anything&quot;.\n sampling_ratio: 1.0\n # Logging settings.\n logging:\n # Specifies log output.\n output: terminal\n # The format to use for log messages.\n format: text\n # Set the logging verbosity level.\n verbosity: INFO\n # A list of field keys to redact when emitting logs.\n #\n # This might be useful to hide certain fields in production logs as they may\n # contain sensitive information, but allow them in testing environment.\n redact_keys: []\n # Metrics settings.\n metrics:\n # How the metrics service identifier defined in `ServiceInfo` is used\n # for this service.\n service_name_format: metric_prefix\n # Whether to report optional metrics in the telemetry server.\n report_optional: false\n # Server settings.\n server:\n # Enables telemetry server\n enabled: true\n # Telemetry server address.\n addr: &quot;127.0.0.1:0&quot;\n# HTTP endpoints configuration.\nendpoints:\n Example endpoint:\n # Address of the endpoint.\n addr: &quot;127.0.0.1:0&quot;\n # Endoint&#039;s URL path routes.\n routes:\n /hello:\n # Status code of the route&#039;s response.\n status_code: 200\n # Content of the route&#039;s response.\n response: World\n /foo:\n # Status code of the route&#039;s response.\n status_code: 403\n # Content of the route&#039;s response.\n response: bar</pre></code>\n <p>Refer to the <a href=\"https://github.com/cloudflare/foundations/blob/347548000cab0ac549f8f23e2a0ce9e1147b7640/examples/http_server/settings.rs\">example web server</a> and documentation for <a href=\"https://docs.rs/foundations/latest/foundations/settings/index.html\">settings</a> and <a href=\"https://docs.rs/foundations/latest/foundations/cli/index.html\">CLI API</a> for more comprehensive examples of how settings can be defined and used with Foundations-provided CLI API.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"wrapping-up\">Wrapping Up</h2>\n <a href=\"#wrapping-up\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>At Cloudflare, we greatly value the contributions of the open source community and are eager to reciprocate by sharing our work. Foundations has been instrumental in reducing our development friction, and we hope it can do the same for others. We welcome external contributions to Foundations, aiming to integrate diverse experiences into the project for the benefit of all.</p><p>If you&#39;re interested in working on projects like Foundations, consider joining our team — <a href=\"https://www.cloudflare.com/en-gb/careers/\">we&#39;re hiring</a>!</p>"],"published_at":[0,"2024-01-24T14:00:17.000+00:00"],"updated_at":[0,"2024-10-09T23:26:53.135Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6LK2SgBru4gS1rh2VRmMEy/62f2ae1c52109773fc10c6f35f494a96/introducing-foundations-our-open-source-rust-service-foundation-library.png"],"tags":[1,[[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"w4e8pkoz9c8xNDVhy9eNe"],"name":[0,"Rust"],"slug":[0,"rust"]}],[0,{"id":[0,"4wT3L4bOFuElnUMa5pXS2G"],"name":[0,"Observability"],"slug":[0,"observability"]}],[0,{"id":[0,"6Mp7ouACN2rT3YjL1xaXJx"],"name":[0,"Security"],"slug":[0,"security"]}],[0,{"id":[0,"598WMMI93tgNRoyjnSrZ2M"],"name":[0,"Oxy"],"slug":[0,"oxy"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Ivan Nikulin"],"slug":[0,"ivan-nikulin"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3YNIwuAmPIbH9iMF69TwJQ/760d0a88dd08e82fcf3fa39d08b29ae6/ivan-nikulin.png"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"Foundations is a foundational Rust library, designed to help scale programs for distributed, production-grade systems."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Introducing Foundations - our open source Rust service foundation library Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/introducing-foundations-our-open-source-rust-service-foundation-library"],"metadata":[0,{"title":[0,"Introducing Foundations - our open source Rust service foundation library"],"description":[0,"Foundations is a foundational Rust library, designed to help scale programs for distributed, production-grade systems."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/71NWXlFqkzGpECA5vVEFu2/67dd375ff52d225e04772a7e6abb96b8/introducing-foundations-our-open-source-rust-service-foundation-library-kZ6qow.png"]}]}],[0,{"id":[0,"5Le8RmeoVTzjhB1qvPodhM"],"title":[0,"Introducing HAR Sanitizer: secure HAR sharing"],"slug":[0,"introducing-har-sanitizer-secure-har-sharing"],"excerpt":[0,"As a follow-up to the most recent Okta breach, we are making a HAR file sanitizer available to everyone, not just Cloudflare customers, at no cost."],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6SLumYs48sPRowoZrb0BCp/84d8ff0d78c5d4a8498edfc136541bbe/image2-8.png\" alt=\"Introducing HAR Sanitizer: secure HAR sharing\" class=\"kg-image\" width=\"1805\" height=\"1007\" loading=\"lazy\"/>\n \n </figure><p>On Wednesday, October 18th, 2023, Cloudflare’s Security Incident Response Team (SIRT) discovered an attack on our systems that originated from an <a href=\"/how-cloudflare-mitigated-yet-another-okta-compromise/\">authentication token stolen from one of Okta’s support systems</a>. No Cloudflare customer information or systems were impacted by the incident, thanks to the real-time detection and rapid action of our Security Incident Response Team (SIRT) in tandem with our <a href=\"https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/\">Zero Trust security posture</a> and use of hardware keys. With that said, we’d rather not repeat the experience — and so we have built a new security tool that can help organizations render this type of attack obsolete for good.</p><p>The bad actor in the Okta breach compromised user sessions by capturing session tokens from administrators at Cloudflare and other impacted organizations. They did this by infiltrating Okta’s customer support system and stealing one of the most common mechanisms for troubleshooting — an HTTP Response Archive (HAR) file.</p><p>HAR files contain a record of a user’s browser session, a kind of step-by-step audit, that a user can share with someone like a help desk agent to diagnose an issue. However, the file can also contain sensitive information that can be used to launch an attack.</p><p>As a follow-up to the Okta breach, we are making a <a href=\"http://har-sanitizer.pages.dev/\">HAR file sanitizer</a> available to everyone, not just Cloudflare customers, at no cost. We are publishing this tool under an <a href=\"https://github.com/cloudflare/har-sanitizer\">open source license</a> and are making it available to any support, engineering or security team. At Cloudflare, we are committed to making the Internet a better place and using HAR files without the threat of stolen sessions should be part of the future of the Internet.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"har-files-a-look-back-in-time\">HAR Files - a look back in time</h2>\n <a href=\"#har-files-a-look-back-in-time\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Imagine being able to rewind time and revisit every single step a user took during a web session, scrutinizing each request and the responses the browser received.</p><p><a href=\"https://en.wikipedia.org/wiki/HAR_%28file_format%29\">HAR (HTTP Archive)</a> files are a JSON formatted archive file of a web browser’s interaction with a web application. HAR files provide a detailed snapshot of every request, including headers, cookies, and other types of data sent to a web server by the browser. This makes them an invaluable resource to troubleshoot web application issues especially for complex, layered web applications.</p><p>The snapshot that a HAR file captures can contain the following information:</p><p><b>Complete Request and Response Headers:</b> Every piece of data sent and received, including method types (GET, POST, etc.), status codes, URLs, cookies, and more.</p><p><b>Payload Content:</b> Details of what was actually exchanged between the client and server, which can be essential for diagnosing issues related to data submission or retrieval.</p><p><b>Timing Information:</b> Precise timing breakdowns of each phase – from DNS lookup, connection time, SSL handshake, to content download – giving insight into performance bottlenecks.</p><p>This information can be difficult to gather from an application’s logs due to the diverse nature of devices, browsers and networks used to access an application. A user would need to take dozens of manual steps. A HAR file gives them a one-click option to share diagnostic information with another party. The file is also standard, providing the developers, support teams, and administrators on the other side of the exchange with a consistent input to their own tooling. This minimizes the frustrating back-and-forth where teams try to recreate a user-reported problem, ensuring that everyone is, quite literally, on the same page.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"har-files-as-an-attack-vector\">HAR files as an attack vector</h2>\n <a href=\"#har-files-as-an-attack-vector\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>HAR files, while powerful, come with a cautionary note. Within the set of information they contain, session cookies make them a target for malicious actors.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"the-role-of-session-cookies\">The Role of Session Cookies</h3>\n <a href=\"#the-role-of-session-cookies\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Before diving into the risks, it&#39;s crucial to understand the role of session cookies. A session cookie is sent from a server and stored on a user&#39;s browser to maintain stateful information across web sessions for that user. In simpler terms, it’s how the browser keeps you logged into an application for a period of time even if you close the page. Generally, these cookies live in local memory on a user’s browser and are not often shared. However, a HAR file is one of the most common ways that a session cookie could be inadvertently shared.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"dangers-of-a-stolen-session-cookie\">Dangers of a stolen session cookie</h3>\n <a href=\"#dangers-of-a-stolen-session-cookie\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>If a HAR file with a valid session cookie is shared, then there are a number of potential security threats that user, and company, may be exposed to:</p><p><b>Unauthorized Access:</b> The biggest risk is unauthorized access. If a HAR file with a session cookie lands in the wrong hands, it grants entry to the user’s account for that application. For platforms that store personal data or financial details, the consequences of such a breach can be catastrophic. Especially if the session cookie of a user with administrative or elevated permissions is stolen.</p><p><b>Session Hijacking:</b> Armed with a session cookie, attackers can impersonate legitimate users, a tactic known as session hijacking. This can lead to a range of malicious activities, from spreading misinformation to siphoning off funds.</p><p><b>Persistent Exposure:</b> Unlike other forms of data, a session cookie&#39;s exposure risk doesn&#39;t necessarily end when a user session does. Depending on the cookie&#39;s lifespan, malicious actors could gain prolonged access, repeatedly compromising a user&#39;s digital interactions.</p><p><b>Gateway to Further Attacks:</b> With access to a user&#39;s session, especially an administrator’s, attackers can probe for other vulnerabilities, exploit platform weaknesses, or jump to other applications.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"mitigating-the-impact-of-a-stolen-har-file\">Mitigating the impact of a stolen HAR file</h2>\n <a href=\"#mitigating-the-impact-of-a-stolen-har-file\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Thankfully, there are ways to render a HAR file inert even if stolen by an attacker. One of the most effective methods is to “sanitize” a HAR file of any session related information before sharing it for debugging purposes.</p><p>The <a href=\"http://har-sanitizer.pages.dev/\">HAR sanitizer</a> we are introducing today allows a user to upload any HAR file, and the tool will strip out any session related cookies or JSON Web Tokens (JWT). The tool is built entirely on Cloudflare Workers, and all sanitization is done client-side which means Cloudflare never sees the full contents of the session token.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/50hGJi9BlyGNoT428LPEJL/1119b4ec03eebf7de4eefa7a5561638c/image1-8.png\" alt=\"\" class=\"kg-image\" width=\"1623\" height=\"499\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h3 id=\"just-enough-sanitization\">Just enough sanitization</h3>\n <a href=\"#just-enough-sanitization\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>By default, the sanitizer will remove all session-related cookies and tokens — but there are some cases where these are essential for troubleshooting. For these scenarios, we are implementing a way to conditionally strip “just enough” data from the HAR file to render them safe, while still giving support teams the information they need.</p><p>The first product we’ve optimized the HAR sanitizer for is <a href=\"https://developers.cloudflare.com/cloudflare-one/policies/access/\">Cloudflare Access</a>. Access relies on a user’s <a href=\"https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/application-token/\">JWT</a> — a compact token often used for secure authentication — to verify that a user should have access to the requested resource. This means a JWT plays a crucial role in troubleshooting issues with Cloudflare Access. We have tuned the HAR sanitizer to strip the cryptographic signature out of the Access JWT, rendering it inert, while still providing useful information for internal admins and Cloudflare support to debug issues.</p><p>Because HAR files can include a diverse array of data types, selectively sanitizing them is not a case of ‘one size fits all’. We will continue to expand support for other popular authentication tools to ensure we strip out “just enough” information.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"whats-next\">What’s next</h2>\n <a href=\"#whats-next\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Over the coming months, we will launch additional security controls in Cloudflare Zero Trust to further mitigate attacks stemming from session tokens stolen from HAR files. This will include:</p><ul><li><p>Enhanced Data Loss Prevention (DLP) file type scanning to include HAR file and session token detections, to ensure users in your organization can not share unsanitized files.</p></li><li><p>Expanded API CASB scanning to detect HAR files with session tokens in collaboration tools like Zendesk, Jira, Drive and O365.</p></li><li><p>Automated HAR sanitization of data in popular collaboration tools.</p></li></ul><p>As always, we continue to expand our Cloudflare One Zero Trust suite to protect organizations of all sizes against an ever-evolving array of threats. Ready to get started? <a href=\"https://www.cloudflare.com/products/zero-trust/\">Sign up here</a> to begin using Cloudflare One at no cost for teams of up to 50 users.</p>"],"published_at":[0,"2023-10-26T14:20:05.000+01:00"],"updated_at":[0,"2024-10-09T23:26:20.326Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3TdJGvwWTHyyCrcIqeMZaq/dc77989249fef7c08feab26b6bac59e4/introducing-har-sanitizer-secure-har-sharing.png"],"tags":[1,[[0,{"id":[0,"1fCflWFtZIDnGI4cd3gRgx"],"name":[0,"Tools"],"slug":[0,"tools"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Kenny Johnson"],"slug":[0,"kenny"],"bio":[0,"Cloudflare Zero Trust PM\nAustin TX"],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4sLNWTlos4CkLxMZ9WzkKg/f0435153a94adcf13569842c86dfeabb/kenny.jpeg"],"location":[0,null],"website":[0,null],"twitter":[0,"@KennyJohnsonATX"],"facebook":[0,null]}]]],"meta_description":[0,"As a follow-up to the most recent Okta breach, we are making a HAR file sanitizer available to everyone, not just Cloudflare customers, at no cost."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Introducing HAR Sanitizer: secure HAR sharing Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/introducing-har-sanitizer-secure-har-sharing"],"metadata":[0,{"title":[0,"Introducing HAR Sanitizer: secure HAR sharing"],"description":[0,"As a follow-up to the most recent Okta breach, we are making a HAR file sanitizer available to everyone, not just Cloudflare customers, at no cost."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7Izkw21T7PAunMYdvmBQEA/172a99354329351adc047f567cc6458e/introducing-har-sanitizer-secure-har-sharing-G5Q7gd.png"]}]}],[0,{"id":[0,"3HZMEE4RsNyQcQ4Zw5dWzl"],"title":[0,"How Pingora keeps count"],"slug":[0,"how-pingora-keeps-count"],"excerpt":[0,"In this blog post, we explain and open source the counting algorithm that powers Pingora. This will be the first of a series of blog posts that share both the Pingora libraries and the ideas behind them"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6SXVdoukjYf7lxtyQLYY6p/03cb231617194c7d8c8bb13557988ad3/image1-13.png\" alt=\"How Pingora keeps count\" class=\"kg-image\" width=\"1999\" height=\"1126\" loading=\"lazy\"/>\n \n </figure><p>A while ago we shared how we replaced NGINX with our in-house proxy, <a href=\"/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/\">Pingora</a>. We promised to share more technical details as well as our open sourcing plan. This blog post will be the first of a series that shares both the code libraries that power Pingora and the ideas behind them.</p><p>Today, we take a look at one of Pingora’s libraries: pingora-limits.</p><p>pingora-limits provides the functionality to count inflight events and estimate the rate of events over time. These functions are commonly used to protect infrastructure and services from being overwhelmed by certain types of malicious or misbehaving requests.</p><p>For example, when an origin server becomes slow or unresponsive, requests will accumulate on our servers, which adds pressure on both our servers and our customers’ servers. With this library, we are able to identify which origins have issues, so that action can be taken without affecting other traffic.</p><p>The problem can be abstracted in a very simple way. The input is a (never ending) stream of different types of events. At any point, the system should be able to tell the number of appearances (or the rate) of a certain type of event.</p><p>In a simple example, colors are used as the type of event. The following is one possible example of a sequence of events:</p><p><code>red, blue, red, orange, green, brown, red, blue,...</code></p><p>In this example, the system should report that “red” appears three times.</p><p>The corresponding algorithms are straightforward to design. One obvious answer is to use a hash table, where the keys are the colors and the values are their corresponding appearances. Whenever a new event appears, the algorithm looks up the hash table and increases the appearance counter. It is not hard to tell that this algorithm’s time complexity is O(1) (per event) and the space complexity O(n) where n is the number of the types of events.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-pingora-does-it\">How Pingora does it</h2>\n <a href=\"#how-pingora-does-it\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The hash table solution is fine in common scenarios, but we believe there are a few things that can be improved.</p><ul><li><p>We observe traffic to millions of different servers when the misbehaving ones are only a few at a given time. It seems a bit wasteful to require space (memory) that holds the counter for all the keys.</p></li><li><p>Concurrently updating the hash table (especially when adding new keys) requires a lock. This behavior potentially forces all concurrent event processing to go through our system serialized. In other words, when lock contention is severe, the lock slows down the system.</p></li></ul><p>The motivation to improve the above algorithm is even stronger considering such algorithms need to be deployed at scale. This algorithm operates on tens of thousands of machines. It handles more than twenty million requests per second. The benefits of efficiency improvement can be significant.</p><p>pingora-limits adopts a different approach: <a href=\"https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch\">count–min sketch</a> (CM sketch) estimation. CM sketch estimates the counts of events in O(1) (per event) but only using O(log(n)) of space (polylogarithmic, to be precise, more details <a href=\"https://dsf.berkeley.edu/cs286/papers/countmin-latin2004.pdf\">here</a>). Because of the simplicity of this algorithm, which we will discuss in a bit, it can be implemented without locks. Therefore, pingora-limits runs much faster and more efficiently compared to the hash table approach discussed earlier.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"cm-sketch\">CM sketch</h3>\n <a href=\"#cm-sketch\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The idea of a CM sketch is similar to a Bloom filter. The mathematical details of the CM sketch can be found in <a href=\"http://dimacs.rutgers.edu/~graham/pubs/papers/cmencyc.pdf\">this paper</a>. In this section, we will just illustrate how it works.</p><p>A CM sketch data structure takes two parameters, H: number of hashes (rows) and N number of counters (columns) per hash (row). The rows and columns form a matrix. The space they take is H*N. Each row has its own <a href=\"https://en.wikipedia.org/wiki/K-independent_hashing\">independent</a> hash function (hash_i()).</p><p>For this example, we use H=3 and N=4:</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-ktyi{background-color:#FFF;text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"150px\">\n<thead>\n <tr>\n <th class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></th>\n <th class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></th>\n <th class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></th>\n <th class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n </tr>\n <tr>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n <td class=\"tg-ktyi\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">0</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>When an event, &quot;red&quot;, arrives, it is counted by every row independently. Each row will use its own hashing function ( hash_i(“red”) ) to choose a column. The counter of the column is increased without <b>worrying about collisions</b> (see the end of this section).</p><p>The table below illustrates a possible state of the matrix after a single “red” event:</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-viy4{background-color:#FFF;color:#172B4D;text-align:left;vertical-align:top}\n.tg .tg-snz2{background-color:#BF2600;color:#172B4D;text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"150px\">\n<thead>\n <tr>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n <th class=\"tg-snz2\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-snz2\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n </tr>\n <tr>\n <td class=\"tg-snz2\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>Then, let’s assume the event &quot;blue&quot; arrives, and we assume it collides with &quot;red&quot; at row 2: both hash to the third slot:</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-viy4{background-color:#FFF;color:#172B4D;text-align:left;vertical-align:top}\n.tg .tg-hk47{background-color:#008DA6;color:#172B4D;text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"150px\">\n<thead>\n <tr>\n <th class=\"tg-hk47\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-hk47\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">2</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n </tr>\n <tr>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-hk47\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">1</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>Let’s say after another series of events, “blue, red, red, red, blue, red”, So far the algorithm observed 5 “red”s and 3 “blue”s in total. Following the algorithm, the estimator eventually becomes:</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-kprd{background-color:#998DD9;color:#172B4D;text-align:left;vertical-align:top}\n.tg .tg-viy4{background-color:#FFF;color:#172B4D;text-align:left;vertical-align:top}\n.tg .tg-hk47{background-color:#008DA6;color:#172B4D;text-align:left;vertical-align:top}\n.tg .tg-snz2{background-color:#BF2600;color:#172B4D;text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"150px\">\n<thead>\n <tr>\n <th class=\"tg-hk47\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">3</span></th>\n <th class=\"tg-snz2\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">5</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n <th class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-kprd\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">8</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n </tr>\n <tr>\n <td class=\"tg-snz2\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">5</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-viy4\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">0</span></td>\n <td class=\"tg-hk47\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#172B4D;background-color:transparent\">3</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>Now, let’s see how the matrix reports the occurrence of each event. In order to retrieve the count of keys, the estimator just returns the minimal value of all the columns to which that key belongs. So the count of red is min(5, 8, 5) = 5 and blue is min(3, 8, 3) = 3.</p><p>This algorithm chooses the cells with the least collisions (via the min() operations). Therefore, collisions between events in single cells are acceptable because as long as there are collision free cells for a given type of event, the counting for that event is accurate.</p><p>The estimator can overestimate when two (or more) keys collide on all slots. Assuming there are only two keys, the probability of their total collision is 1/ N^H (1/64 in this example). On the other hand, it never underestimates because it never loses count of any events.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"practical-implementation\">Practical implementation</h3>\n <a href=\"#practical-implementation\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Because the algorithm only requires hashing, array index and counter increment, it can be implemented in a few lines of code and lock-free.</p><p>The following is a code snippet of how it is implemented in Rust.</p>\n <pre class=\"language-rust\"><code class=\"language-rust\">pub struct Estimator {\n estimator: Box&lt;[(Box&lt;[AtomicIsize]&gt;, RandomState)]&gt;,\n}\n \nimpl Estimator {\n /// Increment `key` by the value given. Return the new estimated value as a result.\n pub fn incr&lt;T: Hash&gt;(&amp;self, key: T, value: isize) -&gt; isize {\n let mut min = isize::MAX;\n for (slot, hasher) in self.estimator.iter() {\n let hash = hash(&amp;key, hasher) as usize;\n let counter = &amp;slot[hash % slot.len()];\n let current = counter.fetch_add(value, Ordering::Relaxed);\n min = std::cmp::min(min, current + value);\n }\n min\n }\n}</pre></code>\n \n <div class=\"flex anchor relative\">\n <h3 id=\"performance\">Performance</h3>\n <a href=\"#performance\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We compare the design above with the two hash table based approaches.</p><ol><li><p>naive: Mutex&lt;HashMap&lt;u32, usize&gt;&gt;. This approach references the simple hash table approach mentioned above. This design requires a lock on every operation.</p></li><li><p>optimized: DashMap&lt;u32, AtomicUsize&gt;. <a href=\"https://docs.rs/dashmap/latest/dashmap/\">DashMap</a> leverages multiple hash tables in order to shard the keys to reduce contentions across different keys. We also use atomic counters here so that counting existing keys won&#39;t need a write lock.</p></li></ol><p>We have two test cases, one that is single threaded and another that is multi-threaded. In both cases, we have one million keys. We generate 100 million events from the keys. The keys are uniformly distributed among the events.</p><p>The results below are performed on Debian VM running on M1 MacBook Pro.</p><p><b>Speed</b>Per event (the incr() function above) timing, lower is better:</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-7s56{background-color:#F4F5F7;text-align:left;vertical-align:top}\n.tg .tg-0lax{text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"100%\">\n<thead>\n <tr>\n <th class=\"tg-7s56\"></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">pingora-limits</span></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">naive</span></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">optimized</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">Single thread</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">10ns</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">51ns</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">43ns</span></td>\n </tr>\n <tr>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">Eight threads</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">212ns</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">1505ns</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">212ns</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>In the single thread case, where there is no lock contention, our approach is 5x faster than the naive one and 4x faster than the optimized one. With multiple threads, there is a high amount of contention. Our approach is similar to the optimized version. Both are 7x faster than the naive one. The reason the performance of pingora-limits and the optimized hash table are similar is because in both approaches the hot path is just updating the atomic counter.</p><p><b>Memory consumption</b>Lower is better. The numbers are collected only from the single threaded test cases for simplicity.</p><!--kg-card-begin: html--><style type=\"text/css\">\n.tg {border-collapse:collapse;border-color:#ccc;border-spacing:0;}\n.tg td{background-color:#fff;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{background-color:#f0f0f0;border-color:#ccc;border-style:solid;border-width:1px;color:#333;\n font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-7s56{background-color:#F4F5F7;text-align:left;vertical-align:top}\n.tg .tg-0lax{text-align:left;vertical-align:top}\n</style>\n<table class=\"tg\" width=\"100%\">\n<thead>\n <tr>\n <th class=\"tg-7s56\"></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">peak memory bytes</span></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\"> total allocations</span></th>\n <th class=\"tg-7s56\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">total allocated bytes</span></th>\n </tr>\n</thead>\n<tbody>\n <tr>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">pingora-limits</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">26,184</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">9</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">26,184</span></td>\n </tr>\n <tr>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">naive</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">53,477,392</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">20</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">71,303,260 </span></td>\n </tr>\n <tr>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">optimized</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">36,211,208 </span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">491</span></td>\n <td class=\"tg-0lax\"><span style=\"font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\">71,307,722</span></td>\n </tr>\n</tbody>\n</table><!--kg-card-end: html--><p>Pingora-limits at peak requires 1/2000 of the memory compared to the naive one and 1/1300 of the memory of the optimized one.</p><p>From the data above, pingora-limits is both CPU and memory efficient.</p><p>The estimator provided by Pingora-limits is a biased estimator because it is possible for it to overestimate the appearance of events.</p><p>In the case of accurate counting, where false positives are absolutely unacceptable, pingora-limits can still be very useful. It can work as a first stage filter where only the events beyond a certain threshold are fed to a hash table to perform accurate counting. In this case, the majority of low frequency event types are filtered out by the filter so that the hash table also consumes little memory without losing any accuracy.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-it-is-used-in-production\">How it is used in production</h2>\n <a href=\"#how-it-is-used-in-production\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In production, pingora uses this library in a few places. The most common one is the connection limit feature. When our servers try to establish too many connections to a single origin server, in order to protect the server and our infrastructure from becoming overloaded, this feature will start rejecting new requests with <a href=\"https://developers.cloudflare.com/support/troubleshooting/cloudflare-errors/troubleshooting-cloudflare-5xx-errors/#error-503-service-temporarily-unavailable\">503 errors</a>.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/WEm9dkrBXLof8baG7ph9e/8ac9205ac8c47ec1a89fabbab848f2ae/image2-4.png\" alt=\"\" class=\"kg-image\" width=\"1999\" height=\"919\" loading=\"lazy\"/>\n \n </figure><p>In this feature every incoming request increases a counter, shared by all other requests with the same customer ID, server IP and the server hostname. When the request finishes, the counter decreases accordingly. If the value of the counter is beyond a certain threshold, the request is rejected with a 503 error response. In our production environment we choose the parameters of the library so that a theoretical collision chance between two unrelated customers is about 1 / 2 ^ 52. Additionally, the rejection threshold is significantly higher than what a healthy customer’s traffic would reach. Therefore, even if multiple customers’ counters collide, it is not likely that the overestimated value would reach the threshold. So a false positive on the connection limit is not likely to happen.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"conclusion\">Conclusion</h2>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Pingora-limits crate is available now on <a href=\"https://github.com/cloudflare/pingora/tree/main/pingora-limits\">GitHub</a>. Both the core functionality and the performance benchmark performed above can be found there.</p><p>In this blog post, we introduced pingora-limits, a library that counts events efficiently. We explained the core idea, which is based on a probabilistic data structure. We also showed through a performance benchmark that the pingora-limits implementation is fast and very efficient for memory consumption.</p><p>Not only that, but we will continue introducing and open sourcing Pingora components and libraries because we believe that sharing the idea behind the code is equally important as sharing the code itself.</p><p>Interested in joining us to help build a better Internet? Our engineering teams are <a href=\"https://www.cloudflare.com/careers/jobs/?department=Engineering\">hiring</a>.</p>"],"published_at":[0,"2023-05-12T14:00:56.000+01:00"],"updated_at":[0,"2024-10-09T23:24:00.575Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2xtRDxFaUOpEAexlXx3jMM/9323156adcf35ea8847c489868d269bd/how-pingora-keeps-count.png"],"tags":[1,[[0,{"id":[0,"4gN0ARax0fHxjtZL07THOe"],"name":[0,"Performance"],"slug":[0,"performance"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"w4e8pkoz9c8xNDVhy9eNe"],"name":[0,"Rust"],"slug":[0,"rust"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Yuchen Wu"],"slug":[0,"yuchen"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Ku4RmxaCrMYgBRauBkpSY/6053cf7e36a88fa5f0ca956780a12863/yuchen.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"In this blogpost, we explain and open source the counting algorithm that powers Pingora. This will be the first of a series of blog posts that share both the Pingora libraries and the ideas behind them."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"How Pingora keeps count Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/how-pingora-keeps-count"],"metadata":[0,{"title":[0,"How Pingora keeps count"],"description":[0,"In this blogpost, we explain and open source the counting algorithm that powers Pingora. This will be the first of a series of blog posts that share both the Pingora libraries and the ideas behind them."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Li1wkHvgbuaUiipjz5Zls/abb33f424e11498d87053160daf41f67/how-pingora-keeps-count-fmLoBk.png"]}]}],[0,{"id":[0,"oXX2cdLdXFvg8ZtUNSaqk"],"title":[0,"How Cloudflare runs Prometheus at scale"],"slug":[0,"how-cloudflare-runs-prometheus-at-scale"],"excerpt":[0,"Here at Cloudflare we run over 900 instances of Prometheus with a total of around 4.9 billion time series.\nOperating such a large Prometheus deployment doesn’t come without challenges .\nIn this blog post we’ll cover some of the issues we hit and how we solved them"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18OlKXbvJgAahs6e81d0yp/ab24a64dda0ab9c41a964c2c1fc74c9b/image6-1.png\" alt=\"How Cloudflare runs Prometheus at scale\" class=\"kg-image\" width=\"1200\" height=\"676\" loading=\"lazy\"/>\n \n </figure><p>We use <a href=\"https://prometheus.io/\">Prometheus</a> to gain insight into all the different pieces of hardware and software that make up our global network. Prometheus allows us to measure health &amp; performance over time and, if there’s anything wrong with any service, let our team know before it becomes a problem.</p><p>At the moment of writing this post we run 916 Prometheus instances with a total of around 4.9 billion time series. Here’s a screenshot that shows exact numbers:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4eQSVoAaOO2Bi1xpCxbkMP/c8086dc6482c75d73cbd4e45cd52e4b8/pasted-image-0--7-.png\" alt=\"\" class=\"kg-image\" width=\"1034\" height=\"825\" loading=\"lazy\"/>\n \n </figure><p>That’s an average of around 5 million time series per instance, but in reality we have a mixture of very tiny and very large instances, with the biggest instances storing around 30 million time series each.</p><p>Operating such a large Prometheus deployment doesn’t come without challenges. In this blog post we’ll cover some of the issues one might encounter when trying to collect many millions of time series per Prometheus instance.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"metrics-cardinality\">Metrics cardinality</h2>\n <a href=\"#metrics-cardinality\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>One of the first problems you’re likely to hear about when you start running your own Prometheus instances is <a href=\"https://en.wikipedia.org/wiki/Cardinality\">cardinality</a>, with the most dramatic cases of this problem being referred to as “cardinality explosion”.</p><p>So let’s start by looking at what cardinality means from Prometheus&#39; perspective, when it can be a problem and some of the ways to deal with it.</p><p>Let’s say we have an application which we want to <a href=\"https://prometheus.io/docs/instrumenting/clientlibs/\">instrument</a>, which means add some observable properties in the form of <a href=\"https://prometheus.io/docs/concepts/metric_types/\">metrics</a> that Prometheus can read from our application. A metric can be anything that you can express as a number, for example:</p><ul><li><p>The speed at which a vehicle is traveling.</p></li><li><p>Current temperature.</p></li><li><p>The number of times some specific event occurred.</p></li></ul><p>To create metrics inside our application we can use one of many Prometheus client libraries. Let’s pick <a href=\"https://github.com/prometheus/client_python\">client_python</a> for simplicity, but the same concepts will apply regardless of the language you use.</p>\n <pre class=\"language-python\"><code class=\"language-python\">from prometheus_client import Counter\n\n# Declare our first metric.\n# First argument is the name of the metric.\n# Second argument is the description of it.\nc = Counter(mugs_of_beverage_total, &#039;The total number of mugs drank.&#039;)\n\n# Call inc() to increment our metric every time a mug was drank.\nc.inc()\nc.inc()</pre></code>\n <p>With this simple code Prometheus client library will create a single metric. For Prometheus to collect this metric we need our application to run an HTTP server and expose our metrics there. The simplest way of doing this is by using functionality provided with client_python itself - see documentation <a href=\"https://github.com/prometheus/client_python#http\">here</a>.</p><p>When Prometheus sends an HTTP request to our application it will receive this response:</p>\n <pre class=\"language-python\"><code class=\"language-python\"># HELP mugs_of_beverage_total The total number of mugs drank.\n# TYPE mugs_of_beverage_total counter\nmugs_of_beverage_total 2</pre></code>\n <p>This format and underlying data model are both covered extensively in Prometheus&#39; own documentation.</p><p>Please see <a href=\"https://prometheus.io/docs/concepts/data_model/\">data model</a> and <a href=\"https://prometheus.io/docs/instrumenting/exposition_formats/\">exposition format</a> pages for more details.</p><p>We can add more metrics if we like and they will all appear in the HTTP response to the metrics endpoint.</p><p>Prometheus metrics can have extra dimensions in form of labels. We can use these to add more information to our metrics so that we can better understand what’s going on.</p><p>With our example metric we know how many mugs were consumed, but what if we also want to know what kind of beverage it was? Or maybe we want to know if it was a cold drink or a hot one? Adding labels is very easy and all we need to do is specify their names. Once we do that we need to pass label values (in the same order as label names were specified) when incrementing our counter to pass this extra information.</p><p>Let’s adjust the example code to do this.</p>\n <pre class=\"language-python\"><code class=\"language-python\">from prometheus_client import Counter\n\nc = Counter(mugs_of_beverage_total, &#039;The total number of mugs drank.&#039;, [&#039;content&#039;, &#039;temperature&#039;])\n\nc.labels(&#039;coffee&#039;, &#039;hot&#039;).inc()\nc.labels(&#039;coffee&#039;, &#039;hot&#039;).inc()\nc.labels(&#039;coffee&#039;, &#039;cold&#039;).inc()\nc.labels(&#039;tea&#039;, &#039;hot&#039;).inc()</pre></code>\n <p>Our HTTP response will now show more entries:</p>\n <pre class=\"language-python\"><code class=\"language-python\"># HELP mugs_of_beverage_total The total number of mugs drank.\n# TYPE mugs_of_beverage_total counter\nmugs_of_beverage_total{content=&quot;coffee&quot;, temperature=&quot;hot&quot;} 2\nmugs_of_beverage_total{content=&quot;coffee&quot;, temperature=&quot;cold&quot;} 1\nmugs_of_beverage_total{content=&quot;tea&quot;, temperature=&quot;hot&quot;} 1</pre></code>\n <p>As we can see we have an entry for each unique combination of labels.</p><p>And this brings us to the definition of cardinality in the context of metrics. Cardinality is the <b>number of unique combinations of all labels</b>. The more labels you have and the more values each label can take, the more unique combinations you can create and the higher the cardinality.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"metrics-vs-samples-vs-time-series\">Metrics vs samples vs time series</h3>\n <a href=\"#metrics-vs-samples-vs-time-series\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Now we should pause to make an important distinction between <i>metrics</i> and <i>time series</i>.</p><p>A metric is an observable property with some defined dimensions (labels). In our example case it’s a Counter class object.</p><p>A time series is an instance of that metric, with a unique combination of all the dimensions (labels), plus a series of timestamp &amp; value pairs - hence the name “time series”. Names and labels tell us what is being observed, while timestamp &amp; value pairs tell us how that observable property changed over time, allowing us to plot graphs using this data.</p><p>What this means is that a single metric will create <b>one or more</b> time series. The number of time series depends purely on the number of labels and the number of all possible values these labels can take.</p><p>Every time we add a new label to our metric we risk multiplying the number of time series that will be exported to Prometheus as the result.</p><p>In our example we have two labels, “content” and “temperature”, and both of them can have two different values. So the maximum number of time series we can end up creating is four (2*2). If we add another label that can also have two values then we can now export up to eight time series (2*2*2). The more labels we have or the more distinct values they can have the more time series as a result.</p><p>If all the label values are controlled by your application you will be able to count the number of all possible label combinations. But the real risk is when you create metrics with label values coming from the outside world.</p><p>If instead of beverages we tracked the number of HTTP requests to a web server, and we used the request path as one of the label values, then anyone making a huge number of random requests could force our application to create a huge number of time series. To avoid this it’s in general best to never accept label values from untrusted sources.</p><p>To make things more complicated you may also hear about “samples” when reading Prometheus documentation. A sample is something in between metric and time series - it’s a time series value for a specific timestamp. Timestamps here can be explicit or implicit. If a sample lacks any explicit timestamp then it means that the sample represents the most recent value - it’s the current value of a given time series, and the timestamp is simply the time you make your observation at.</p><p>If you look at the HTTP response of our example metric you’ll see that none of the returned entries have timestamps. There’s no timestamp anywhere actually. This is because the Prometheus server itself is responsible for timestamps. When Prometheus collects metrics it records the time it started each collection and then it will use it to write timestamp &amp; value pairs for each time series.</p><p>That’s why what our application exports isn’t really metrics or time series - it’s samples.</p><p>Confusing? Let’s recap:</p><ul><li><p>We start with a <b>metric</b> - that’s simply a definition of something that we can observe, like the number of mugs drunk.</p></li><li><p>Our metrics are exposed as a HTTP response. That response will have a list of <b>samples</b> - these are individual instances of our metric (represented by name &amp; labels), plus the current value.</p></li><li><p>When Prometheus collects all the samples from our HTTP response it adds the timestamp of that collection and with all this information together we have a <b>time series</b>.</p></li></ul>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5lTDb6LR4pGWmpAC2mrug2/4771271c4b28e30930be606bfd5b4213/blog-4.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"1446\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h3 id=\"cardinality-related-problems\">Cardinality related problems</h3>\n <a href=\"#cardinality-related-problems\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Each time series will cost us resources since it needs to be kept in memory, so the more time series we have, the more resources metrics will consume. This is true both for client libraries and Prometheus server, but it’s more of an issue for Prometheus itself, since a single Prometheus server usually collects metrics from many applications, while an application only keeps its own metrics.</p><p>Since we know that the more labels we have the more time series we end up with, you can see when this can become a problem. Simply adding a label with two distinct values to all our metrics might double the number of time series we have to deal with. Which in turn will double the memory usage of our Prometheus server. If we let Prometheus consume more memory than it can physically use then it will crash.</p><p>This scenario is often described as “cardinality explosion” - some metric suddenly adds a huge number of distinct label values, creates a huge number of time series, causes Prometheus to run out of memory and you lose all observability as a result.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-is-prometheus-using-memory\">How is Prometheus using memory?</h2>\n <a href=\"#how-is-prometheus-using-memory\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>To better handle problems with cardinality it’s best if we first get a better understanding of how Prometheus works and how time series consume memory.</p><p>For that let’s follow all the steps in the life of a time series inside Prometheus.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-one-http-scrape\">Step one - HTTP scrape</h3>\n <a href=\"#step-one-http-scrape\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The process of sending HTTP requests from Prometheus to our application is called “scraping”. Inside the Prometheus configuration file we define a <a href=\"https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config\">“scrape config”</a> that tells Prometheus where to send the HTTP request, how often and, optionally, to apply extra processing to both requests and responses.</p><p>It will record the time it sends HTTP requests and use that later as the timestamp for all collected time series.</p><p>After sending a request it will parse the response looking for all the samples exposed there.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2g84l6ioJCxTsF64iZoHr8/8ae8b1a7140517cd8a8eb330482c7377/blog-1.png\" alt=\"\" class=\"kg-image\" width=\"1282\" height=\"775\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-two-new-time-series-or-an-update\">Step two - new time series or an update?</h3>\n <a href=\"#step-two-new-time-series-or-an-update\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Once Prometheus has a list of samples collected from our application it will save it into <a href=\"https://pkg.go.dev/github.com/prometheus/prometheus/tsdb\">TSDB</a> - Time Series DataBase - the database in which Prometheus keeps all the time series.</p><p>But before doing that it needs to first check which of the samples belong to the time series that are already present inside TSDB and which are for completely new time series.</p><p>As we mentioned before a time series is generated from metrics. There is a single time series for each unique combination of metrics labels.</p><p>This means that Prometheus must check if there’s already a time series with identical name and exact same set of labels present. Internally time series names are just another label called __name__, so there is no practical distinction between name and labels. Both of the representations below are different ways of exporting the same time series:</p>\n <pre class=\"language-python\"><code class=\"language-python\">mugs_of_beverage_total{content=&quot;tea&quot;, temperature=&quot;hot&quot;} 1\n{__name__=&quot;mugs_of_beverage_total&quot;, content=&quot;tea&quot;, temperature=&quot;hot&quot;} 1</pre></code>\n <p>Since everything is a label Prometheus can simply hash all labels using sha256 or any other algorithm to come up with a single ID that is unique for each time series.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4cXuu38kxa2u51z8Me0aBP/9dcc5694aef45e5a65a19dd4d7a7b8a7/blog-2.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"922\" loading=\"lazy\"/>\n \n </figure><p>Knowing that it can quickly check if there are any time series already stored inside TSDB that have the same hashed value. Basically our labels hash is used as a primary key inside TSDB.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-three-appending-to-tsdb\">Step three - appending to TSDB</h3>\n <a href=\"#step-three-appending-to-tsdb\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Once TSDB knows if it has to insert new time series or update existing ones it can start the real work.</p><p>Internally all time series are stored <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1604-L1616\">inside a map</a> on a structure called <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L65\">Head</a>. That map uses labels hashes as keys and a structure called <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1827\">memSeries</a> as values. Those memSeries objects are storing all the time series information. The struct definition for memSeries is fairly big, but all we really need to know is that it has a copy of all the <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1831\">time series labels</a> and <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1843-L1844\">chunks</a> that hold all the samples (timestamp &amp; value pairs).</p><p>Labels are stored once per each memSeries instance.</p><p>Samples are stored inside chunks using <a href=\"https://prometheus.io/blog/2016/05/08/when-to-use-varbit-chunks/#what-is-varbit-encoding\">&quot;varbit&quot; encoding</a> which is a lossless compression scheme optimized for time series data. Each chunk represents a series of samples for a <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1969\">specific time range</a>. This helps Prometheus query data faster since all it needs to do is first locate the memSeries instance with labels matching our query and then find the chunks responsible for time range of the query.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2hE16dmyvOEjiTJUGnDVTy/11b37f49775fac13165d75ff3d7b1c78/blog-5.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"952\" loading=\"lazy\"/>\n \n </figure><p><a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/cmd/prometheus/main.go#L300-L301\">By default</a> Prometheus will create a chunk per each <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/db.go#L53\">two hours</a> of <b>wall clock</b>. So there would be a chunk for: 00:00 - 01:59, 02:00 - 03:59, 04:00 - 05:59, …, 22:00 - 23:59.</p><p>There’s only one chunk that we can append to, it’s called the “Head Chunk”. It’s the chunk responsible for the most recent time range, including the time of our scrape. Any other chunk holds historical samples and therefore is read-only.</p><p>There is a maximum of <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head_append.go#L1337\">120</a> samples each chunk can hold. This is because once we have more than 120 samples on a chunk efficiency of “varbit” encoding drops. TSDB <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head_append.go#L1371-L1386\">will try to estimate</a> when a given chunk will reach 120 samples and it will set the maximum allowed time for current Head Chunk accordingly.</p><p>If we try to append a sample with a timestamp higher than the maximum allowed time for current Head Chunk, then TSDB will create a new Head Chunk and calculate a new maximum time for it based on the rate of appends.</p><p>All chunks must be aligned to those two hour slots of wall clock time, so if TSDB was building a chunk for 10:00-11:59 and it was already “full” at 11:30 then it would create an extra chunk for the 11:30-11:59 time range.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1U540qt4t8r4OjMdKpBEIf/93aa0eb5e741683f71ef0eff002d6b1f/blog-6.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"418\" loading=\"lazy\"/>\n \n </figure><p>Since the default Prometheus scrape interval is one minute it would take two hours to reach 120 samples.</p><p>What this means is that using Prometheus defaults each memSeries should have a single chunk with 120 samples on it for every two hours of data.</p><p>Going back to our time series - at this point Prometheus either creates a new memSeries instance or uses already existing memSeries. Once it has a memSeries instance to work with it will append our sample to the Head Chunk. This might require Prometheus to create a new chunk if needed.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-four-memory-mapping-old-chunks\">Step four - memory-mapping old chunks</h3>\n <a href=\"#step-four-memory-mapping-old-chunks\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>After a few hours of Prometheus running and scraping metrics we will likely have more than one chunk on our time series:</p><ul><li><p>One “Head Chunk” - containing up to two hours of the last two hour wall clock slot.</p></li><li><p>One or more for historical ranges - these chunks are only for reading, Prometheus won’t try to append anything here.</p></li></ul><p>Since all these chunks are stored in memory Prometheus will try to reduce memory usage by writing them to disk and memory-mapping. The advantage of doing this is that memory-mapped chunks don’t use memory unless TSDB needs to read them.</p><p>The Head Chunk is never memory-mapped, it’s always stored in memory.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-five-writing-blocks-to-disk\">Step five - writing blocks to disk</h3>\n <a href=\"#step-five-writing-blocks-to-disk\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Up until now all time series are stored entirely in memory and the more time series you have, the higher Prometheus memory usage you’ll see. The only exception are memory-mapped chunks which are offloaded to disk, but will be read into memory if needed by queries.</p><p>This allows Prometheus to scrape and store thousands of samples per second, our biggest instances are appending 550k samples per second, while also allowing us to query all the metrics simultaneously.</p><p>But you can’t keep everything in memory forever, even with memory-mapping parts of data.</p><p>Every two hours Prometheus will persist chunks from memory onto the disk. This process is also aligned with the wall clock but <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1489-L1494\">shifted by one hour</a>.</p><p>When using Prometheus defaults and assuming we have a single chunk for each two hours of wall clock we would see this:</p><ul><li><p>02:00 - create a new chunk for 02:00 - 03:59 time range</p></li><li><p>03:00 - write a block for 00:00 - 01:59</p></li><li><p>04:00 - create a new chunk for 04:00 - 05:59 time range</p></li><li><p>05:00 - write a block for 02:00 - 03:59</p></li><li><p>…</p></li><li><p>22:00 - create a new chunk for 22:00 - 23:59 time range</p></li><li><p>23:00 - write a block for 20:00 - 21:59</p></li></ul>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Bt2X8rR89TBWekMduXrSZ/d1cfc97d7fe7a60c431b123027755714/blog-7.png\" alt=\"\" class=\"kg-image\" width=\"1435\" height=\"1031\" loading=\"lazy\"/>\n \n </figure><p>Once a chunk is written into a block it is removed from memSeries and thus from memory. Prometheus will keep each block on disk for the configured retention period.</p><p>Blocks will eventually be “compacted”, which means that Prometheus will take multiple blocks and merge them together to form a single block that covers a bigger time range. This process helps to reduce disk usage since each block has an index taking a good chunk of disk space. By merging multiple blocks together, big portions of that index can be reused, allowing Prometheus to store more data using the same amount of storage space.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"step-six-garbage-collection\">Step six - garbage collection</h3>\n <a href=\"#step-six-garbage-collection\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>After a chunk was written into a block and removed from memSeries we might end up with an instance of memSeries that has no chunks. This would happen if any time series was no longer being exposed by any application and therefore there was no scrape that would try to append more samples to it.</p><p>A common pattern is to export software versions as a build_info metric, Prometheus itself does this too:</p>\n <pre class=\"language-python\"><code class=\"language-python\">prometheus_build_info{version=&quot;2.42.0&quot;} 1</pre></code>\n <p>When Prometheus 2.43.0 is released this metric would be exported as:</p>\n <pre class=\"language-python\"><code class=\"language-python\">prometheus_build_info{version=&quot;2.43.0&quot;} 1</pre></code>\n <p>Which means that a time series with version=”2.42.0” label would no longer receive any new samples.</p><p>Once the last chunk for this time series is written into a block and removed from the memSeries instance we have no chunks left. This means that our memSeries still consumes some memory (mostly labels) but doesn’t really do anything.</p><p>To get rid of such time series Prometheus will run “head garbage collection” (remember that Head is the structure holding all memSeries) right after writing a block. This garbage collection, among other things, will look for any <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/tsdb/head.go#L1642-L1648\">time series without a single chunk</a> and remove it from memory.</p><p>Since this happens after writing a block, and writing a block happens in the middle of the chunk window (two hour slices aligned to the wall clock) the only memSeries this would find are the ones that are “orphaned” - they received samples before, but not anymore.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"what-does-this-all-mean\">What does this all mean?</h3>\n <a href=\"#what-does-this-all-mean\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>TSDB used in Prometheus is a special kind of database that was highly optimized for a very specific workload:</p><ul><li><p>Time series scraped from applications are kept in memory.</p></li><li><p>Samples are compressed using encoding that works best if there are continuous updates.</p></li><li><p>Chunks that are a few hours old are written to disk and removed from memory.</p></li><li><p>When time series disappear from applications and are no longer scraped they still stay in memory until all chunks are written to disk and garbage collection removes them.</p></li></ul><p>This means that Prometheus is most efficient when continuously scraping the same time series over and over again. It’s least efficient when it scrapes a time series just once and never again - doing so comes with a significant memory usage overhead when compared to the amount of information stored using that memory.</p><p>If we try to visualize how the perfect type of data Prometheus was designed for looks like we’ll end up with this:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/58Sdl0opvl2leashpMABHR/2fbe55566085307e632f284c9f487695/blog-13.png\" alt=\"\" class=\"kg-image\" width=\"930\" height=\"636\" loading=\"lazy\"/>\n \n </figure><p>A few continuous lines describing some observed properties.</p><p>If, on the other hand, we want to visualize the type of data that Prometheus is the least efficient when dealing with, we’ll end up with this instead:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/peimfAo5FJ8Z7U4N9pYtm/0779cb39815baea5a3ee3018d746c398/blog-14.png\" alt=\"\" class=\"kg-image\" width=\"930\" height=\"636\" loading=\"lazy\"/>\n \n </figure><p>Here we have single data points, each for a different property that we measure.</p><p>Although you can tweak some of Prometheus&#39; behavior and tweak it more for use with short lived time series, by passing one of <a href=\"https://github.com/prometheus/prometheus/blob/v2.42.0/cmd/prometheus/main.go#L300-L305\">the hidden flags</a>, it’s generally discouraged to do so. These flags are only exposed for testing and might have a negative impact on other parts of Prometheus server.</p><p>To get a better understanding of the impact of a short lived time series on memory usage let’s take a look at another example.</p><p>Let’s see what happens if we start our application at 00:25, allow Prometheus to scrape it once while it exports:</p>\n <pre class=\"language-python\"><code class=\"language-python\">prometheus_build_info{version=&quot;2.42.0&quot;} 1</pre></code>\n <p>And then immediately after the first scrape we upgrade our application to a new version:</p>\n <pre class=\"language-python\"><code class=\"language-python\">prometheus_build_info{version=&quot;2.43.0&quot;} 1</pre></code>\n <p>At 00:25 Prometheus will create our memSeries, but we will have to wait until Prometheus writes a block that contains data for 00:00-01:59 and runs garbage collection before that memSeries is removed from memory, which will happen at 03:00.</p><p>This single sample (data point) will create a time series instance that will stay in memory for over two and a half hours using resources, just so that we have a single timestamp &amp; value pair.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/42ihJDKAWyj7hT4DicZoxV/abff5645102c8c22da0c1ad8e811445a/blog-8.png\" alt=\"\" class=\"kg-image\" width=\"717\" height=\"421\" loading=\"lazy\"/>\n \n </figure><p>If we were to continuously scrape a lot of time series that only exist for a very brief period then we would be slowly accumulating a lot of memSeries in memory until the next garbage collection.</p><p>Looking at memory usage of such Prometheus server we would see this pattern repeating over time:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/qccLTfcv0dyJ6bFnEkkd6/ad547942173e2f6333412f694fbb0faa/blog-15.png\" alt=\"\" class=\"kg-image\" width=\"1241\" height=\"888\" loading=\"lazy\"/>\n \n </figure><p>The important information here is that <b>short lived time series are expensive</b>. A time series that was only scraped once is <b>guaranteed to live in Prometheus for one to three hours</b>, depending on the exact time of that scrape.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"the-cost-of-cardinality\">The cost of cardinality</h2>\n <a href=\"#the-cost-of-cardinality\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>At this point we should know a few things about Prometheus:</p><ul><li><p>We know what a metric, a sample and a time series is.</p></li><li><p>We know that the more labels on a metric, the more time series it can create.</p></li><li><p>We know that each time series will be kept in memory.</p></li><li><p>We know that time series will stay in memory for a while, even if they were scraped only once.</p></li></ul><p>With all of that in mind we can now see the problem - a <b>metric with high cardinality</b>, especially one with label values that come from the outside world, can easily create a huge number of <b>time series</b> in a very short time, causing <b>cardinality explosion</b>. This would inflate Prometheus memory usage, which can cause Prometheus server to crash, if it uses all available physical memory.</p><p>To get a better idea of this problem let’s adjust our example metric to track HTTP requests.</p><p>Our metric will have a single label that stores the request path.</p>\n <pre class=\"language-python\"><code class=\"language-python\">from prometheus_client import Counter\n\nc = Counter(http_requests_total, &#039;The total number of HTTP requests.&#039;, [&#039;path&#039;])\n\n# HTTP request handler our web server will call\ndef handle_request(path):\n c.labels(path).inc()\n ...</pre></code>\n <p>If we make a single request using the curl command:</p>\n <pre class=\"language-curl\"><code class=\"language-curl\">&gt; curl https://app.example.com/index.html</pre></code>\n <p>We should see these time series in our application:</p>\n <pre class=\"language-python\"><code class=\"language-python\"># HELP http_requests_total The total number of HTTP requests.\n# TYPE http_requests_total counter\nhttp_requests_total{path=&quot;/index.html&quot;} 1</pre></code>\n <p>But what happens if an evil hacker decides to send a bunch of random requests to our application?</p>\n <pre class=\"language-curl\"><code class=\"language-curl\">&gt; curl https://app.example.com/jdfhd5343\n&gt; curl https://app.example.com/3434jf833\n&gt; curl https://app.example.com/1333ds5\n&gt; curl https://app.example.com/aaaa43321</pre></code>\n <p>Extra time series would be created:</p>\n <pre class=\"language-python\"><code class=\"language-python\"># HELP http_requests_total The total number of HTTP requests.\n# TYPE http_requests_total counter\nhttp_requests_total{path=&quot;/index.html&quot;} 1\nhttp_requests_total{path=&quot;/jdfhd5343&quot;} 1\nhttp_requests_total{path=&quot;/3434jf833&quot;} 1\nhttp_requests_total{path=&quot;/1333ds5&quot;} 1\nhttp_requests_total{path=&quot;/aaaa43321&quot;} 1</pre></code>\n <p>With 1,000 random requests we would end up with 1,000 time series in Prometheus. If our metric had more labels and all of them were set based on the request payload (HTTP method name, IPs, headers, etc) we could easily end up with millions of time series.</p><p>Often it doesn’t require any malicious actor to cause cardinality related problems. A common class of mistakes is to have an error label on your metrics and pass raw error objects as values.</p>\n <pre class=\"language-python\"><code class=\"language-python\">from prometheus_client import Counter\n\nc = Counter(errors_total, &#039;The total number of errors.&#039;, [error])\n\ndef my_func:\n try:\n ...\n except Exception as err:\n c.labels(err).inc()</pre></code>\n <p>This works well if errors that need to be handled are generic, for example “Permission Denied”:</p>\n <pre class=\"language-python\"><code class=\"language-python\">errors_total{error=&quot;Permission Denied&quot;} 1</pre></code>\n <p>But if the error string contains some task specific information, for example the name of the file that our application didn’t have access to, or a TCP connection error, then we might easily end up with high cardinality metrics this way:</p>\n <pre class=\"language-python\"><code class=\"language-python\">errors_total{error=&quot;file not found: /myfile.txt&quot;} 1\nerrors_total{error=&quot;file not found: /other/file.txt&quot;} 1\nerrors_total{error=&quot;read udp 127.0.0.1:12421-&gt;127.0.0.2:443: i/o timeout&quot;} 1\nerrors_total{error=&quot;read udp 127.0.0.1:14743-&gt;127.0.0.2:443: i/o timeout&quot;} 1</pre></code>\n <p>Once scraped all those time series will stay in memory for a minimum of one hour. It’s very easy to keep accumulating time series in Prometheus until you run out of memory.</p><p>Even Prometheus&#39; own <a href=\"https://github.com/prometheus/client_golang/security/advisories/GHSA-cg3q-j54f-5p7p\">client libraries had bugs</a> that could expose you to problems like this.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-much-memory-does-a-time-series-need\">How much memory does a time series need?</h2>\n <a href=\"#how-much-memory-does-a-time-series-need\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Each time series stored inside Prometheus (as a memSeries instance) consists of:</p><ul><li><p>Copy of all labels.</p></li><li><p>Chunks containing samples.</p></li><li><p>Extra fields needed by Prometheus internals.</p></li></ul><p>The amount of memory needed for labels will depend on the number and length of these. The more labels you have, or the longer the names and values are, the more memory it will use.</p><p>The way labels are stored internally by Prometheus also matters, but that’s something the user has no control over. There is an open pull request which improves memory usage of labels by <a href=\"https://github.com/prometheus/prometheus/pull/10991\">storing all labels as a single string</a>.</p><p>Chunks will consume more memory as they slowly fill with more samples, after each scrape, and so the memory usage here will follow a cycle - we start with low memory usage when the first sample is appended, then memory usage slowly goes up until a new chunk is created and we start again.</p><p>You can calculate how much memory is needed for your time series by running this query on your Prometheus server:</p>\n <pre class=\"language-python\"><code class=\"language-python\">go_memstats_alloc_bytes / prometheus_tsdb_head_series</pre></code>\n <p>Note that your Prometheus server must be configured to scrape itself for this to work.</p><p>Secondly this calculation is based on all memory used by Prometheus, not only time series data, so it’s just an approximation. Use it to get a rough idea of how much memory is used per time series and don’t assume it’s that exact number.</p><p>Thirdly Prometheus is written in <a href=\"https://go.dev/\">Golang</a> which is a language with garbage collection. The actual amount of physical memory needed by Prometheus will usually be higher as a result, since it will include unused (garbage) memory that needs to be freed by Go runtime.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"protecting-prometheus-from-cardinality-explosions\">Protecting Prometheus from cardinality explosions</h2>\n <a href=\"#protecting-prometheus-from-cardinality-explosions\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Prometheus does offer some options for dealing with high cardinality problems. There are a <a href=\"https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config\">number of options</a> you can set in your scrape configuration block. Here is the extract of the relevant options from Prometheus documentation:</p>\n <pre class=\"language-python\"><code class=\"language-python\"># An uncompressed response body larger than this many bytes will cause the\n# scrape to fail. 0 means no limit. Example: 100MB.\n# This is an experimental feature, this behaviour could\n# change or be removed in the future.\n[ body_size_limit: &lt;size&gt; | default = 0 ]\n# Per-scrape limit on number of scraped samples that will be accepted.\n# If more than this number of samples are present after metric relabeling\n# the entire scrape will be treated as failed. 0 means no limit.\n[ sample_limit: &lt;int&gt; | default = 0 ]\n\n# Per-scrape limit on number of labels that will be accepted for a sample. If\n# more than this number of labels are present post metric-relabeling, the\n# entire scrape will be treated as failed. 0 means no limit.\n[ label_limit: &lt;int&gt; | default = 0 ]\n\n# Per-scrape limit on length of labels name that will be accepted for a sample.\n# If a label name is longer than this number post metric-relabeling, the entire\n# scrape will be treated as failed. 0 means no limit.\n[ label_name_length_limit: &lt;int&gt; | default = 0 ]\n\n# Per-scrape limit on length of labels value that will be accepted for a sample.\n# If a label value is longer than this number post metric-relabeling, the\n# entire scrape will be treated as failed. 0 means no limit.\n[ label_value_length_limit: &lt;int&gt; | default = 0 ]\n\n# Per-scrape config limit on number of unique targets that will be\n# accepted. If more than this number of targets are present after target\n# relabeling, Prometheus will mark the targets as failed without scraping them.\n# 0 means no limit. This is an experimental feature, this behaviour could\n# change in the future.\n[ target_limit: &lt;int&gt; | default = 0 ]</pre></code>\n <p>Setting all the label length related limits allows you to avoid a situation where extremely long label names or values end up taking too much memory.</p><p>Going back to our metric with error labels we could imagine a scenario where some operation returns a huge error message, or even stack trace with hundreds of lines. If such a stack trace ended up as a label value it would take a lot more memory than other time series, potentially even megabytes. Since labels are copied around when Prometheus is handling queries this could cause significant memory usage increase.</p><p>Setting label_limit provides some cardinality protection, but even with just one label name and huge number of values we can see high cardinality. Passing sample_limit is the ultimate protection from high cardinality. It enables us to enforce a hard limit on the number of time series we can scrape from each application instance.</p><p>The downside of all these limits is that <b>breaching any of them will cause an error for the entire scrape</b>.</p><p>If we configure a sample_limit of 100 and our metrics response contains 101 samples, then Prometheus <b>won’t scrape anything at all</b>. This is a deliberate design decision made by Prometheus developers.</p><p>The main motivation seems to be that dealing with partially scraped metrics is difficult and you’re better off treating failed scrapes as incidents.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-does-cloudflare-deal-with-high-cardinality\">How does Cloudflare deal with high cardinality?</h2>\n <a href=\"#how-does-cloudflare-deal-with-high-cardinality\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We have hundreds of data centers spread across the world, each with dedicated Prometheus servers responsible for scraping all metrics.</p><p>Each Prometheus is scraping a few hundred different applications, each running on a few hundred servers.</p><p>Combined that’s a lot of different metrics. It’s not difficult to accidentally cause cardinality problems and in the past we’ve dealt with a fair number of issues relating to it.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"basic-limits\">Basic limits</h3>\n <a href=\"#basic-limits\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The most basic layer of protection that we deploy are scrape limits, which we enforce on all configured scrapes. These are the sane defaults that 99% of application exporting metrics would never exceed.</p><p>By default we allow up to 64 labels on each time series, which is way more than most metrics would use.</p><p>We also limit the length of label names and values to 128 and 512 characters, which again is more than enough for the vast majority of scrapes.</p><p>Finally we do, by default, set sample_limit to 200 - so each application can export up to 200 time series without any action.</p><p>What happens when somebody wants to export more time series or use longer labels? All they have to do is set it explicitly in their scrape configuration.</p><p>Those limits are there to catch accidents and also to make sure that if any application is exporting a high number of time series (more than 200) the team responsible for it knows about it. This helps us avoid a situation where applications are exporting thousands of times series that aren’t really needed. Once you cross the 200 time series mark, you should start thinking about your metrics more.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"ci-validation\">CI validation</h3>\n <a href=\"#ci-validation\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The next layer of protection is checks that run in CI (Continuous Integration) when someone makes a pull request to add new or modify existing scrape configuration for their application.</p><p>These checks are designed to ensure that we have enough capacity on all Prometheus servers to accommodate extra time series, if that change would result in extra time series being collected.</p><p>For example, if someone wants to modify sample_limit, let’s say by changing existing limit of 500 to 2,000, for a scrape with 10 targets, that’s an increase of 1,500 per target, with 10 targets that’s 10*1,500=15,000 extra time series that might be scraped. Our CI would check that all Prometheus servers have spare capacity for at least 15,000 time series before the pull request is allowed to be merged.</p><p>This gives us confidence that we won’t overload any Prometheus server after applying changes.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"our-custom-patches\">Our custom patches</h3>\n <a href=\"#our-custom-patches\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>One of the most important layers of protection is a set of patches we maintain on top of Prometheus. There is an <a href=\"https://github.com/prometheus/prometheus/pull/11124\">open pull request</a> on the Prometheus repository. This patchset consists of two main elements.</p><p>First is the patch that allows us to enforce a limit on the total number of time series TSDB can store at any time. There is no equivalent functionality in a standard build of Prometheus, if any scrape produces some samples they will be appended to time series inside TSDB, creating new time series if needed.</p><p>This is the standard flow with a scrape that doesn’t set any sample_limit:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4oxj5PicoVqieXzyDzlq2C/cbbf6a05d61adad33da574b05a297bde/blog-10.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"716\" loading=\"lazy\"/>\n \n </figure><p>With our patch we tell TSDB that it’s allowed to store up to N time series in total, from all scrapes, at any time. So when TSDB is asked to append a new sample by any scrape, it will first check how many time series are already present.</p><p>If the total number of stored time series is below the configured limit then we append the sample as usual.</p><p>The difference with standard Prometheus starts when a new sample is about to be appended, but TSDB already stores the maximum number of time series it’s allowed to have. Our patched logic will then check if the sample we’re about to append belongs to a time series that’s already stored inside TSDB or is it a new time series that needs to be created.</p><p>If the time series already exists inside TSDB then we allow the append to continue. If the time series doesn’t exist yet and our append would create it (a new memSeries instance would be created) then we skip this sample. We will also signal back to the scrape logic that some samples were skipped.</p><p>This is the modified flow with our patch:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3luOmih7ZayqWukDhgoC5S/98cade46c7b5da6bb3a85ae6877532bb/blog-11.png\" alt=\"\" class=\"kg-image\" width=\"1306\" height=\"1600\" loading=\"lazy\"/>\n \n </figure><p>By running <i>“go_memstats_alloc_bytes / prometheus_tsdb_head_series”</i> query we know how much memory we need per single time series (on average), we also know how much physical memory we have available for Prometheus on each server, which means that we can easily calculate the rough number of time series we can store inside Prometheus, taking into account the fact the there’s garbage collection overhead since Prometheus is written in Go:</p><p><i>memory available to Prometheus / bytes per time series = our capacity</i></p><p>This doesn’t capture all complexities of Prometheus but gives us a rough estimate of how many time series we can expect to have capacity for.</p><p>By setting this limit on all our Prometheus servers we know that it will never scrape more time series than we have memory for. This is the last line of defense for us that avoids the risk of the Prometheus server crashing due to lack of memory.</p><p>The second patch modifies how Prometheus handles sample_limit - with our patch instead of failing the entire scrape it simply ignores excess time series. If we have a scrape with sample_limit set to 200 and the application exposes 201 time series, then all except one final time series will be accepted.</p><p>This is the standard Prometheus flow for a scrape that has the sample_limit option set:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2d8iEKXhwkrME4gR7IYEvm/71faa4cdb0781465b35e2c303e4c4e3e/blog-9.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"1005\" loading=\"lazy\"/>\n \n </figure><p>The entire scrape either succeeds or fails. Prometheus simply counts how many samples are there in a scrape and if that’s more than sample_limit allows it will fail the scrape.</p><p>With our custom patch we don’t care how many samples are in a scrape. Instead we count time series as we append them to TSDB. Once we appended sample_limit number of samples we start to be selective.</p><p>Any excess samples (after reaching sample_limit) will only be appended if they belong to time series that are already stored inside TSDB.</p><p>The reason why we still allow appends for some samples even after we’re above sample_limit is that <b>appending samples to existing time series is cheap</b>, it’s just adding an extra timestamp &amp; value pair.</p><p><b>Creating new time series on the other hand is a lot more expensive</b> - we need to allocate new memSeries instances with a copy of all labels and keep it in memory for at least an hour.</p><p>This is how our modified flow looks:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3FstYUSQFx7G4uY1jIdlME/dbba3406fe4fcfea15410b4431bb01ca/blog-12.png\" alt=\"\" class=\"kg-image\" width=\"1306\" height=\"1600\" loading=\"lazy\"/>\n \n </figure><p>Both patches give us two levels of protection.</p><p>The TSDB limit patch protects the entire Prometheus from being overloaded by too many time series.</p><p>This is because the only way to stop time series from eating memory is to prevent them from being appended to TSDB. Once they’re in TSDB it’s already too late.</p><p>While the sample_limit patch stops individual scrapes from using too much Prometheus capacity, which could lead to creating too many time series in total and exhausting total Prometheus capacity (enforced by the first patch), which would in turn affect all other scrapes since some new time series would have to be ignored. At the same time our patch gives us graceful degradation by capping time series from each scrape to a certain level, rather than failing hard and dropping all time series from affected scrape, which would mean losing all observability of affected applications.</p><p>It’s also worth mentioning that without our TSDB total limit patch we could keep adding new scrapes to Prometheus and that alone could lead to exhausting all available capacity, even if each scrape had sample_limit set and scraped fewer time series than this limit allows.</p><p>Extra metrics exported by Prometheus itself tell us if any scrape is exceeding the limit and if that happens we alert the team responsible for it.</p><p>This also has the benefit of allowing us to self-serve capacity management - there’s no need for a team that signs off on your allocations, if CI checks are passing then we have the capacity you need for your applications.</p><p>The main reason why we prefer graceful degradation is that we want our engineers to be able to deploy applications and their metrics with confidence without being subject matter experts in Prometheus. That way even the most inexperienced engineers can start exporting metrics without constantly wondering <i>“Will this cause an incident?”</i>.</p><p>Another reason is that trying to stay on top of your usage can be a challenging task. It might seem simple on the surface, after all you just need to stop yourself from creating too many metrics, adding too many labels or setting label values from untrusted sources.</p><p>In reality though this is as simple as trying to ensure your application doesn’t use too many resources, like CPU or memory - you can achieve this by simply allocating less memory and doing fewer computations. It doesn’t get easier than that, until you actually try to do it. The more any application does for you, the more useful it is, the more resources it might need. Your needs or your customers&#39; needs will evolve over time and so you can’t just draw a line on how many bytes or cpu cycles it can consume. If you do that, the line will eventually be redrawn, many times over.</p><p>In general, having more labels on your metrics allows you to gain more insight, and so the more complicated the application you&#39;re trying to <a href=\"https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/\">monitor</a>, the more need for extra labels.</p><p>In addition to that in most cases we don’t see all possible label values at the same time, it’s usually a small subset of all possible combinations. For example our errors_total metric, which we used in example before, might not be present at all until we start seeing some errors, and even then it might be just one or two errors that will be recorded. This holds true for a lot of labels that we see are being used by engineers.</p><p>This means that looking at how many time series an application could potentially export, and how many it actually exports, gives us two completely different numbers, which makes capacity planning a lot harder.</p><p>Especially when dealing with big applications maintained in part by multiple different teams, each exporting some metrics from their part of the stack.</p><p>For that reason we do tolerate some percentage of short lived time series even if they are not a perfect fit for Prometheus and cost us more memory.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"documentation\">Documentation</h3>\n <a href=\"#documentation\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Finally we maintain a set of internal documentation pages that try to guide engineers through the process of scraping and working with metrics, with a lot of information that’s specific to our environment.</p><p>Prometheus and PromQL (Prometheus Query Language) are conceptually very simple, but this means that all the complexity is hidden in the interactions between different elements of the whole metrics pipeline.</p><p>Managing the entire lifecycle of a metric from an engineering perspective is a complex process.</p><p>You must define your metrics in your application, with names and labels that will allow you to work with resulting time series easily. Then you must configure Prometheus scrapes in the correct way and deploy that to the right Prometheus server. Next you will likely need to create recording and/or alerting rules to make use of your time series. Finally you will want to create a dashboard to visualize all your metrics and be able to spot trends.</p><p>There will be traps and room for mistakes at all stages of this process. We covered some of the most basic pitfalls in our previous blog post on Prometheus - <a href=\"/monitoring-our-monitoring/\">Monitoring our monitoring</a>. In the same blog post we also mention one of the tools we use to help our engineers write valid Prometheus alerting rules.</p><p>Having good internal documentation that covers all of the basics specific for our environment and most common tasks is very important. Being able to answer <i>“How do I X?”</i> yourself without having to wait for a subject matter expert allows everyone to be more productive and move faster, while also avoiding Prometheus experts from answering the same questions over and over again.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"closing-thoughts\">Closing thoughts</h2>\n <a href=\"#closing-thoughts\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Prometheus is a great and reliable tool, but dealing with high cardinality issues, especially in an environment where a lot of different applications are scraped by the same Prometheus server, can be challenging.</p><p>We had a fair share of problems with overloaded Prometheus instances in the past and developed a number of tools that help us deal with them, including custom patches.</p><p>But the key to tackling high cardinality was better understanding how Prometheus works and what kind of usage patterns will be problematic.</p><p>Having better insight into Prometheus internals allows us to maintain a fast and reliable observability platform without too much red tape, and the tooling we’ve developed around it, some of which is <a href=\"https://github.com/cloudflare/pint\">open sourced</a>, helps our engineers avoid most common pitfalls and deploy with confidence.</p>"],"published_at":[0,"2023-03-03T14:00:00.000+00:00"],"updated_at":[0,"2024-10-09T23:22:57.684Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/0R6SWOx3ZoukS6BCz8i47/afb220b8de184ffef672f4b0ab8a78d3/how-cloudflare-runs-prometheus-at-scale.png"],"tags":[1,[[0,{"id":[0,"27rpZLTb1hq5wyGAno2aw1"],"name":[0,"Prometheus"],"slug":[0,"prometheus"]}],[0,{"id":[0,"4wT3L4bOFuElnUMa5pXS2G"],"name":[0,"Observability"],"slug":[0,"observability"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"2UVIYusJwlvsmPYl2AvSuR"],"name":[0,"Deep Dive"],"slug":[0,"deep-dive"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Lukasz Mierzwa"],"slug":[0,"lukasz"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ctPUzMNTSvnvNYl9wDRoh/02cf0464fa2f410a5c5a7bb105088110/lukasz.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"Here at Cloudflare we run over 900 instances of Prometheus with a total of around 4.9 billion time series.\nOperating such a large Prometheus deployment doesn’t come without challenges .\nIn this blog post we’ll cover some of the issues we hit and how we solved them."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"How Cloudflare runs Prometheus at scale Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/how-cloudflare-runs-prometheus-at-scale"],"metadata":[0,{"title":[0,"How Cloudflare runs Prometheus at scale"],"description":[0,"Here at Cloudflare we run over 900 instances of Prometheus with a total of around 4.9 billion time series.\nOperating such a large Prometheus deployment doesn’t come without challenges .\nIn this blog post we’ll cover some of the issues we hit and how we solved them."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1oUS34Kn1MJZQ55JuBBHQM/e3b79f11fa7e3ae64fa72c6f618b1dc1/how-cloudflare-runs-prometheus-at-scale-tIZ7E2.png"]}]}],[0,{"id":[0,"4PaKE41nYJr4E5KTs8vven"],"title":[0,"Introducing workerd: the Open Source Workers runtime"],"slug":[0,"workerd-open-source-workers-runtime"],"excerpt":[0,"workerd is the JavaScript/Wasm runtime code that powers Cloudflare Workers, now open source under the Apache 2.0 license"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3SSkRQRya35sx2YIjcJmdh/3e1db1507ffa7800ba7c7cf5edd482a1/image1-49.png\" alt=\"Introducing workerd: the Open Source Workers runtime\" class=\"kg-image\" width=\"1800\" height=\"1013\" loading=\"lazy\"/>\n \n </figure><p>Today I&#39;m proud to introduce the first beta release of workerd, the JavaScript/Wasm runtime based on the same code that powers Cloudflare Workers. workerd is Open Source under the Apache License version 2.0.</p><p>workerd shares most of its code with the runtime that powers Cloudflare Workers, but with some changes designed to make it more portable to other environments. The name &quot;workerd&quot; (pronounced &quot;worker dee&quot;) comes from the Unix tradition of naming servers with a &quot;-d&quot; suffix standing for &quot;daemon&quot;. The name is not capitalized because it is a program name, which are traditionally lower-case in Unix-like environments.</p><!--kg-card-begin: html--><p><style>\n\t\tbutton {\n\t\t\tcolor: #ffffff;\n\t\t\tbackground-color: #1166ee;\n\t\t\tfont-size: 19px;\n\t\t\tborder: 1px solid #2d63c8;\n\t\t\tpadding: 15px 50px;\n\t\t\tcursor: pointer\n\t\t}\n\t\tbutton:hover {\n\t\t\tcolor: #ffffff;\n\t\t\tbackground-color: #377af6;\n\t\t}\n\t</style>\n\t<a href=\"https://github.com/cloudflare/workerd\" open=\"_new\"><button type=\"button\" name=\"GitHubCTA\">Find the code on GitHub</button></a></p><!--kg-card-end: html-->\n <div class=\"flex anchor relative\">\n <h2 id=\"what-its-for\">What it's for</h2>\n <a href=\"#what-its-for\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <div class=\"flex anchor relative\">\n <h3 id=\"self-hosting-workers\">Self-hosting Workers</h3>\n <a href=\"#self-hosting-workers\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd can be used to self-host applications that you&#39;d otherwise run on Cloudflare Workers. It is intended to be a production-ready web server for this purpose. workerd has been designed to be unopinionated about hosting environments, so that it should fit nicely into whatever server/VM/container hosting and orchestration system you prefer. It&#39;s just a web server.</p><p>Workers has always been based on standardized APIs, so that code is not locked into Cloudflare, and <a href=\"/introducing-the-wintercg/\">we work closely with other runtimes to promote compatibility</a>. workerd provides another option to ensure that applications built on Workers can run anywhere, by leveraging the same underlying code to get exact, &quot;bug-for-bug&quot; compatibility.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"local-development-and-testing\">Local development and testing</h3>\n <a href=\"#local-development-and-testing\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd is also designed to facilitate realistic local testing of Workers. Up until now, this has been achieved using <a href=\"https://miniflare.dev/\">Miniflare</a>, which simulated the Workers API within a Node.js environment. Miniflare has worked well, but in a number of cases its behavior did not exactly match Workers running on Cloudflare. With the release of workerd, Miniflare and the Wrangler CLI tool will now be able to provide a more accurate simulation by leveraging the same runtime code we use in production.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"programmable-proxies\">Programmable proxies</h3>\n <a href=\"#programmable-proxies\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd can act as an application host, a proxy, or both. It supports both forward and reverse proxy modes. In all cases, JavaScript code can be used to intercept and process requests and responses before forwarding them on. Traditional web servers and proxies have used bespoke configuration languages with quirks that are hard to master. Programming proxies in JavaScript instead provides more power while making the configuration easier to write and understand.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"what-it-is\">What it is</h2>\n <a href=\"#what-it-is\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd is not just another way to run JavaScript and Wasm. Our runtime is uniquely designed in a number of ways.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"server-first\">Server-first</h3>\n <a href=\"#server-first\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Many non-browser JavaScript and Wasm runtimes are designed to be general-purpose: you can use them to build command-line apps, local GUI apps, servers, or anything in between. workerd is not. It specifically focuses on servers, in particular (for now, at least) HTTP servers.</p><p>This means in particular that workerd-based applications are event-driven at the top level. Applications do not open listen sockets and accept connections from them; instead, the runtime pushes events to the application. It may seem like a minor difference, but this basic change in perspective directly enables many of the features below.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"web-standard-apis\">Web standard APIs</h3>\n <a href=\"#web-standard-apis\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Wherever possible, Workers (and workerd in particular) offers the same standard APIs found in web browsers, such as Fetch, URL, WebCrypto, and others. This means that code built on workerd is more likely to be portable to browsers as well as to other standards-based runtimes. When Workers launched five years ago, it was unusual for a non-browser to offer web APIs, but we are pleased to see that the broader JavaScript ecosystem is now converging on them.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"nanoservices\">Nanoservices</h3>\n <a href=\"#nanoservices\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd is a nanoservice runtime. What does that mean?</p><p>Microservices have become popular over the last decade as a way to split monolithic servers into smaller components that could be maintained and deployed independently. For example, a company that offers several web applications with a common user authentication flow might have a separate team that maintains the authentication logic. In a monolithic model, the authentication logic might have been offered to the application teams as a library. However, this could be frustrating for the maintainers of that logic, as making any change might require waiting for every application team to deploy an update to their respective server. By splitting the authentication logic into a separate server that all the others talk to, the authentication team is able to deploy changes on their own schedule.</p><p>However, microservices have a cost. What was previously a fast library call instead now requires communicating over a network. In addition to added overhead, this communication requires configuration and administration to ensure security and reliability. These costs become greater as the codebase is split into more and more services. Eventually, the costs outweigh the benefits.</p><p>Nanoservices are a new model that achieve the benefits of independent deployment with overhead closer to that of library calls. With workerd, many Workers can be configured to run in the same process. Each Worker runs in a separate &quot;isolate&quot;, which gives the appearance of running independently of the others: each isolate loads separate code and has its own global scope. However, when one Worker explicitly sends a request to another Worker, the destination Worker actually runs in the same thread with zero latency. So, it performs more like a function call.</p><p>With nanoservices, teams can now break their code into many more independently-deployed pieces without worrying about the overhead.</p><p>(Some in the industry prefer to call nanoservices &quot;functions&quot;, implying that each individual function making up an application could be its own service. I feel, however, that this puts too much emphasis on syntax rather than logical functionality. That said, it is the same concept.)</p><p>To really make nanoservices work well, we had to minimize the baseline overhead of each service. This required designing workerd very differently from most other runtimes, so that common resources could be shared between services as much as possible. First, as mentioned, we run many nanoservices within a single process, to share basic process overhead and minimize context switching costs. A second big architectural difference between workerd and other runtimes is how it handles built-in APIs. Many runtimes implement significant portions of their built-in APIs in JavaScript, which must then be loaded separately into each isolate. workerd does not; all the APIs are implemented in native code, so that all isolates may share the same copy of that code. These design choices would be difficult to retrofit into another runtime, and indeed these needs are exactly why we chose to build a custom runtime for Workers from the start.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"homogeneous-deployment\">Homogeneous deployment</h3>\n <a href=\"#homogeneous-deployment\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>In a typical microservices model, you might deploy different microservices to containers running across a cluster of machines, connected over a local network. You might manually choose how many containers to dedicate to each service, or you might configure some form of auto-scaling based on resource usage.</p><p>workerd offers an alternative model: <b>Every machine runs every service.</b></p><p>workerd&#39;s nanoservices are much lighter-weight than typical containers. As a result, it&#39;s entirely reasonable to run a very large number of them – hundreds, maybe thousands – on a single server. This in turn means that you can simply deploy every service to every machine in your fleet.</p><p>Homogeneous deployment means that you don&#39;t have to worry about scaling individual services. Instead, you can simply load balance requests across the entire cluster, and scale the cluster as needed. Overall, this can greatly reduce the amount of administration work needed.</p><p>Cloudflare itself has used the homogeneous model on our network since the beginning. Every one of Cloudflare&#39;s edge servers runs our entire software stack, so any server can answer any kind of request on its own. We&#39;ve found it works incredibly well. This is why services on Cloudflare – including ones that use Workers – are able to go from no traffic at all to millions of requests per second instantly without trouble.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"capability-bindings-cleaner-configuration-and-ssrf-safety\">Capability bindings: cleaner configuration and SSRF safety</h3>\n <a href=\"#capability-bindings-cleaner-configuration-and-ssrf-safety\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd takes a different approach to most runtimes – indeed, to most software development platforms – in how an application accesses external resources.</p><p>Most development platforms start from assuming that the application can talk to the whole world. It is up to the application to figure out exactly what it wants to talk to, and name it in some global namespace, such as using a URL. So, an application server that wants to talk to the authentication microservice might use code like this:</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">// Traditional approach without capability bindings.\nfetch(&quot;https://auth-service.internal-network.example.com/api&quot;, {\n method: &quot;POST&quot;,\n body: JSON.stringify(authRequest),\n headers: { &quot;Authorization&quot;: env.AUTH_SERVICE_TOKEN }\n});</pre></code>\n <p>In workerd, we do things differently. An application starts out with no ability to talk to the rest of the world, and must be configured with specific <i>capability bindings</i> that provide it access to <i>specific</i> external resources. So, an application which needs to be able to talk to the authentication service would be configured with a binding called authService, and the code would look something like this:</p>\n <pre class=\"language-javascript\"><code class=\"language-javascript\">// Capability-based approach. Hostname doesn&#039;t matter; all\n// requests to AUTH_SERVICE.fetch() go to the auth service.\nenv.AUTH_SERVICE.fetch(&quot;https://auth/api&quot;, {\n method: &quot;POST&quot;,\n body: JSON.stringify(authRequest),\n});</pre></code>\n <p>This may at first appear to be a trivial difference. In both cases, we have to use configuration to <a href=\"https://www.cloudflare.com/learning/access-management/what-is-access-control/\">control access</a> to external services. In the traditional approach, we&#39;d provide access tokens (and probably the service&#39;s hostname) as environment variables. In the new approach, the environment goes a bit further to provide a full-fledged object. Is this just syntax sugar?</p><p>It turns out, this slight change has huge advantages:</p><p>First, we can now restrict the global fetch() function to accept only publicly-routable URLs. <b>This makes applications totally immune to SSRF attacks!</b> You cannot trick an application into accessing an internal service unintentionally if the code to access internal services is explicitly different. (In fact, the global fetch() is itself backed by a binding, which can be configured. workerd defaults to connecting it to the public internet, but you can also override it to permit private addresses if you want, or to route to a specific proxy service, or to be blocked entirely.)</p><p>With that done, we now have an interesting property: All internal services which an application uses <i>must</i> be configurable. This means:</p><ul><li><p>You can easily see a complete list of the internal services an application talks to, without reading all the code.</p></li><li><p>You can always replace these services with mocks for testing purposes.</p></li><li><p>You can always configure an application to authenticate itself differently (e.g. client certificates) or use a different back end, without changing code.</p></li></ul><p>The receiving end of a binding benefits, too. Take the authentication service example, above. The auth service may be another Worker running in workerd as a nanoservice. In this case, the auth service does not need to be bound to any actual network address. Instead, it may be made available strictly to other Workers through their bindings. In this case, the authentication service doesn&#39;t necessarily need to verify that a request received came from an allowed client – because only allowed clients are able to send requests to it in the first place.</p><p>Overall, capability bindings allow simpler code that is secure by default, more composable, easier to test, and easier to understand and maintain.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"always-backwards-compatible\">Always backwards compatible</h3>\n <a href=\"#always-backwards-compatible\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Cloudflare Workers has a hard rule against ever breaking a live Worker running in production. This same dedication to backwards compatibility extends to workerd.</p><p>workerd shares <a href=\"/backwards-compatibility-in-cloudflare-workers/\">Workers&#39; compatibility date system</a> to manage breaking changes. Every Worker must be configured with a &quot;compatibility date&quot;. The runtime then ensures that the API behaves exactly as it did on that date. At your leisure, you may <a href=\"https://developers.cloudflare.com/workers/platform/compatibility-dates\">check the documentation</a> to see if new breaking changes are introduced at a future date, and update your code for them. Most such changes are minor and most code won&#39;t require any changes. However, you are never obliged to update. Old dates will continue to be supported by newer versions of workerd. It is always safe to update workerd itself without updating your code.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"what-its-not\">What it's not</h2>\n <a href=\"#what-its-not\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>To avoid misleading or disappointing anyone, I need to take a moment to call out what workerd is not.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"workerd-is-not-a-secure-sandbox\">workerd is not a Secure Sandbox</h3>\n <a href=\"#workerd-is-not-a-secure-sandbox\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>It&#39;s important to note that workerd is not, on its own, a secure way to run possibly-malicious code. If you wish to run code you don&#39;t trust using workerd, you must enclose it in an additional sandboxing layer, such as a virtual machine configured for sandboxing.</p><p>workerd itself is designed such that a Worker should not be able to access any external resources to which it hasn&#39;t been granted a capability. However, a complete sandbox solution not only must be designed to restrict access, but also must account for the possibility of bugs – both in software and in hardware. workerd on its own is not sufficient to protect against hardware bugs like Spectre, nor can it adequately defend against the possibility of vulnerabilities in V8 or in workerd&#39;s own code.</p><p>The Cloudflare Workers service uses the same code found in workerd, but adds many additional layers of security on top to harden against such bugs. <a href=\"/mitigating-spectre-and-other-security-threats-the-cloudflare-workers-security-model/\">I described some of these in a past blog post</a>. However, these measures are closely tied to our particular environment. For example, we rely on build automation to push V8 patches to production immediately upon becoming available; we separate customers according to risk profile; we rely on non-portable kernel features and assumptions about the host system to enforce security and resource limits. All of this is very specific to our environment, and cannot be packaged up in a reusable way.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"workerd-is-not-an-independent-project\">workerd is not an independent project</h3>\n <a href=\"#workerd-is-not-an-independent-project\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>workerd is the core of Cloudflare Workers, a fast-moving project developed by a dedicated team at Cloudflare. We are not throwing code over the wall to forget about, nor are we expecting volunteers to do our jobs for us. workerd&#39;s GitHub repository will be the canonical source used by Cloudflare Workers and our team will be doing much of their work directly in this repository. Just like V8 is developed primarily by the Chrome team for use in Chrome, workerd will be developed primarily by the Cloudflare Workers team for use in Cloudflare Workers.</p><p>This means we cannot promise that external contributions will sit on a level playing field with internal ones. Code reviews take time, and work that is needed for Cloudflare Workers will take priority. We also cannot promise we will accept every feature contribution. Even if the code is already written, reviews and maintenance have a cost. Within Cloudflare, we have a product management team who carefully evaluates what features we should and shouldn&#39;t offer, and plenty of ideas generated internally ultimately don&#39;t make the cut.</p><p>If you want to contribute a big new feature to workerd, your best bet is to talk to us before you write code, by raising an issue on GitHub early to get input. That way, you can find out if we&#39;re likely to accept a PR before you write it. We also might be able to give hints on how best to implement.</p><p>It&#39;s also important to note that while workerd&#39;s internal interfaces may sometimes appear clean and reusable, we cannot make any guarantee that those interfaces won&#39;t completely change on a whim. If you are trying to build on top of workerd internals, you will need to be prepared either to accept a fair amount of churn, or pin to a specific version.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"workerd-is-not-an-off-the-shelf-edge-compute-platform\">workerd is not an off-the-shelf edge compute platform</h3>\n <a href=\"#workerd-is-not-an-off-the-shelf-edge-compute-platform\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>As hinted above, the full Cloudflare Workers service involves a lot of technology beyond workerd itself, including additional security, deployment mechanisms, orchestration, and so much more. workerd itself is a portion of our runtime codebase, which is itself a small (albeit critical) piece of the overall Cloudflare Workers service.</p><p>We are pleased, though, that this means it is possible for us to release this code under a permissive Open Source license.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"try-the-beta\">Try the Beta</h2>\n <a href=\"#try-the-beta\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>As of this blog post, workerd is in beta. If you want to try it out,</p><!--kg-card-begin: html--><p><style>\n\t\tbutton {\n\t\t\tcolor: #ffffff;\n\t\t\tbackground-color: #1166ee;\n\t\t\tfont-size: 19px;\n\t\t\tborder: 1px solid #2d63c8;\n\t\t\tpadding: 15px 50px;\n\t\t\tcursor: pointer\n\t\t}\n\t\tbutton:hover {\n\t\t\tcolor: #ffffff;\n\t\t\tbackground-color: #377af6;\n\t\t}\n\t</style>\n\t<a href=\"https://github.com/cloudflare/workerd\" open=\"_new\"><button type=\"button\" name=\"GitHubCTA\">Find the readme on GitHub</button></a></p><!--kg-card-end: html-->"],"published_at":[0,"2022-09-27T14:01:00.000+01:00"],"updated_at":[0,"2024-10-09T23:20:24.987Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/VX91xrqI6AzEjNGHSKfuu/0e812df0fb490e0ca9380d79a98facb4/workerd-open-source-workers-runtime.png"],"tags":[1,[[0,{"id":[0,"1Cv5JjXzKWKEA10JdYbXu1"],"name":[0,"Birthday Week"],"slug":[0,"birthday-week"]}],[0,{"id":[0,"6hbkItfupogJP3aRDAq6v8"],"name":[0,"Cloudflare Workers"],"slug":[0,"workers"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"5cye1Bh5KxFh3pKSnX8Dsy"],"name":[0,"Serverless"],"slug":[0,"serverless"]}],[0,{"id":[0,"6QktrXeEFcl4e2dZUTZVGl"],"name":[0,"Product News"],"slug":[0,"product-news"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Kenton Varda"],"slug":[0,"kenton-varda"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1FFs4T2j1RyvxasKOkkdtP/e7bd05ce89c560a545853000a25da9bc/kenton-varda.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,"@kentonvarda"],"facebook":[0,null]}]]],"meta_description":[0,"workerd is the JavaScript/Wasm runtime code that powers Cloudflare Workers, now open source under the Apache 2.0 license."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Introducing workerd: the Open Source Workers runtime Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/workerd-open-source-workers-runtime"],"metadata":[0,{"title":[0,"Introducing workerd: the Open Source Workers runtime"],"description":[0,"workerd is the JavaScript/Wasm runtime code that powers Cloudflare Workers, now open source under the Apache 2.0 license."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qpwFTmp4d0o6QSJqOubMR/8d9ebfd20dddbff7fe3bc9aac614d6bd/workerd-open-source-workers-runtime-0ROsbB.png"]}]}],[0,{"id":[0,"5ILynCMGbg5sa61rxooh2H"],"title":[0,"Open sourcing our fork of PgBouncer"],"slug":[0,"open-sourcing-our-fork-of-pgbouncer"],"excerpt":[0,"We are releasing our internal fork of PgBouncer, filled with authentication bug fixes and new features around per user and connection pool isolation"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/32baeHUVDC0ZxTOXAlJyVV/fb472bc46e9fa4ae0bc3238e1673b03b/Magic-Nat.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"890\" loading=\"lazy\"/>\n \n </figure><p>Cloudflare operates highly available Postgres production clusters across multiple data centers, supporting the transactional workloads of our core service offerings such as our DNS Resolver, Firewall, and DDoS Protection.</p><p>Multiple PgBouncer instances sit at the front of the gateway layer per each cluster, acting as a TCP proxy that provides Postgres connection pooling. PgBouncer’s pooling enables upstream applications to connect to Postgres, without having to constantly open and close connections (expensive) at the database level, while also reducing the number of Postgres connections used. Each tenant acquires client-side connections from PgBouncer instead of Postgres directly.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ncwV2JtgSNvA6ZSFRi9c9/cab4b0f421cad51acab4adc5890d48a5/Frame-673.png\" alt=\"\" class=\"kg-image\" width=\"807\" height=\"578\" loading=\"lazy\"/>\n \n </figure><p>PgBouncer will hold a pool of maximum server-side connections to Postgres, allocating those across multiple tenants to prevent Postgres connection starvation. From here, PgBouncer will forward backend queries to HAProxy, which load balances across Postgres primary and read replicas.</p><p>As an intern at Cloudflare I got to work on improving how our database clusters behave under load and open source the resulting code.</p><p>We run our Postgres infrastructure in non-containerized, bare metal environments which consequently leads to multitenant resource contention between Postgres users. To enforce stricter tenant performance isolation at the database level (CPU time utilized, memory consumption, disk IO operations), we’d like to configure and enforce connection limits per user and connection pool at PgBouncer.</p><p>To do that we had to add features and fix bugs in PgBouncer. Rather than continue to maintain a private fork we are open sourcing our code for others to use.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"authentication-rejection\">Authentication Rejection</h3>\n <a href=\"#authentication-rejection\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The PgBouncer connection pooler offers options to enforce server connection pool size limits (effective concurrency) per user via static configuration. However, an authentication bug upstream prevented these features from correctly working when Postgres was set to use HBA authentication. Administrators who sensibly use server-side authentication could not take advantage of these user-level features.</p><p>This ongoing issue has also been experienced by others in the open-source community:</p><p><a href=\"https://github.com/pgbouncer/pgbouncer/issues/484\">https://github.com/pgbouncer/pgbouncer/issues/484</a><a href=\"https://github.com/pgbouncer/pgbouncer/issues/596\">https://github.com/pgbouncer/pgbouncer/issues/596</a></p>\n <div class=\"flex anchor relative\">\n <h3 id=\"root-cause\">Root Cause</h3>\n <a href=\"#root-cause\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>PgBouncer needs a Postgres user’s password when proxying submitted queries from client connection to a Postgres server connection. PgBouncer will fetch a user’s Postgres password defined in userlist.txt (auth_file) when a user first logs in to compare against the provided password. However, if the user is not defined in userlist.txt, Pgbouncer will fetch their password from the Postgres <a href=\"https://www.postgresql.org/docs/current/view-pg-shadow.html\">pg_shadow</a> system view for comparison. This password will be used when PgBouncer subsequently forwards queries from this user to Postgres. The same applies when Postgres is configured to use HBA authentication.</p><p>Following serious debugging efforts and time spent in GDB, we found that multiple user objects are typically created for a single real user: via configuration loading from the [users] section and upon the user’s first login. In PgBouncer, any users requiring a shadow auth query would be stored under their respective database struct instance, whereas any user with a password defined in userlist.txt would be stored globally. Because the non-authenticated user already existed in memory after being parsed from the [users] section, PgBouncer assumed that the user was defined in userlist.txt, where the shadow authentication query could be skipped. It would not bother to fetch and set the user’s password upon first login, resulting in an empty user password. This is why subsequent queries submitted by the user would be rejected with authentication failure at Postgres.</p><p>To solve this, we simplified the code to globally store all users in one place rather than store different types of users (requiring different methods of authentication) in a disaggregated fashion per database or globally. Also, rather than assuming a user is authenticated if they merely exist, we keep track of whether the user requires authentication via auth query or from fetching their password from userlist.txt. This depends on how they were created.</p><p>We saw the value in troubleshooting and fixing these issues; it would unlock an entire class of features in PgBouncer for our use cases, while benefiting many in the open-source community.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"new-features\">New Features</h3>\n <a href=\"#new-features\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We’ve also done work to implement and support additional features in PgBouncer to enforce stricter tenant performance isolation.</p><p>Previously, PgBouncer would only prevent tenants from exceeding preconfigured limits, not particularly helpful when it’s too late and a user is misbehaving or already has too many connections. PgBouncer now supports enforcing or shrinking per user connection pool limits at runtime, <b>where it is most critically needed</b> to throttle tenants who are issuing a burst of expensive queries, or are hogging connections from other tenants. We’ve also implemented new administrative commands to throttle the maximum connections per user or per pool at runtime.</p><p>PgBouncer also now supports statically configuring and dynamically enforcing connection limits per connection pool. This feature is extremely important in order to granularly throttle a tenant’s misbehaving connection pool without throttling and reducing availability on its other non-misbehaving pools.</p><h5>PgBouncer Configuration</h5>\n <pre class=\"language-bash\"><code class=\"language-bash\">[users]\ndns_service_user = max_user_connections=60\nfirewall_service_user = max_user_connections=80\n[pools]\nuser1.database1 = pool_size=90</pre></code>\n <h5>PgBouncer Runtime Commands</h5>\n <pre class=\"language-bash\"><code class=\"language-bash\">SET USER dns_service_user = ‘max_user_connections=40’;\nSET POOL dns_service_user.dns_db = ‘pool_size=30’;</pre></code>\n <p>These new features required major refactoring around how PgBouncer stores users, databases weakly referenced and stored passwords of different users, and how we enforce killing server side connections while still in use.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"conclusion\">Conclusion</h3>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>We are committed to improving PgBouncer in open source and contributing all of our features to benefit the wider community. If you are interested, please consider contributing to our <a href=\"https://github.com/cloudflare/cf-pgbouncer\">open source PgBouncer fork</a>. After all, it is the community that makes PgBouncer possible!</p>"],"published_at":[0,"2022-08-26T15:30:37.000+01:00"],"updated_at":[0,"2024-10-10T00:44:34.253Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4keLxyHjI12YJ5CVM3yFSj/260d71dfee4d208664bd668f1720fc31/open-sourcing-our-fork-of-pgbouncer.png"],"tags":[1,[[0,{"id":[0,"3RrKxQpSeoUVWXO1gT9jsU"],"name":[0,"Edge Database"],"slug":[0,"edge-database"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"3vILtHLIQU7d8BaWzXAPv4"],"name":[0,"Internship Experience"],"slug":[0,"internship-experience"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Justin Kwan"],"slug":[0,"justin-kwan"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3pJS0rBUAqQzyYi7wFPj1P/9ffdf191845fcf63208d7456fb5b298d/justin-kwan.jpeg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,null],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Open sourcing our fork of PgBouncer Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/open-sourcing-our-fork-of-pgbouncer"],"metadata":[0,{"title":[0],"description":[0],"imgPreview":[0,""]}]}],[0,{"id":[0,"7939BvPeIO2CLZlTxYLIGv"],"title":[0,"Building and using Managed Components with WebCM"],"slug":[0,"building-using-managed-components-webcm"],"excerpt":[0,"This is how Managed Components can be useful for you right now, if you manage a website or if you’re building third-party tools"],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7koVQfRWyZu6sTtWX8hQBt/2d979591dd76e7392bdfc72e0b242c59/image1-6.png\" alt=\"Building and using Managed Components with WebCM\" class=\"kg-image\" width=\"1800\" height=\"1013\" loading=\"lazy\"/>\n \n </figure><p>Managed Components are here to shake up the way third-party tools integrate with websites. Two months ago we announced that <a href=\"/zaraz-open-source-managed-components-and-webcm/\">we’re open sourcing parts of the most innovative technologies behind Cloudflare Zaraz</a>, making them accessible and usable to everyone online. Since then, we’ve been working hard to make sure that the code is well documented and all pieces are fun and easy to use. In this article, I want to show you how Managed Components can be useful for you right now, if you manage a website or if you’re building third-party tools. But before we dive in, let’s talk about the past.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"third-party-scripts-are-a-threat-to-your-website\">Third-party scripts are a threat to your website</h3>\n <a href=\"#third-party-scripts-are-a-threat-to-your-website\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>For decades, if you wanted to add an analytics tool to your site, a chat widget, a conversion pixel or any other kind of tool – you needed to include an external script. That usually meant adding some code like this to your website:</p>\n <pre class=\"language-html\"><code class=\"language-html\">&lt;script src=”https://example.com/script.js”&gt;&lt;/script&gt;</pre></code>\n <p>If you think about it – it’s a pretty terrible idea. Not only that you’re now asking the browser to connect to <i>another</i> server, fetch and execute <i>more</i> JavaScript code – you’re also completely giving up the control on your website. How much do you really trust this script? And how much do you trust that the script’s server wasn’t hacked, or will never get hacked in the future? In the previous blog post we showed <a href=\"/zaraz-use-workers-to-make-third-party-tools-secure-and-fast/#how-third-party-tools-work-today\">how including one script usually results in more network calls</a>, hogging the browser and slowing everything down. But the worst thing about these scripts is that they are completely unrestricted: JavaScript code running in the browser can hijack users, steal their credentials or credit card information, use their devices to mine cryptocurrencies, manipulate your website, leak PII, and more. Since the code in those scripts is usually minified, it’s practically impossible for you to read it and figure out what it’s doing.</p><p>Managed Components change all that. Like apps on your phone, they’re built around a permissions system. You decide if you allow a component to connect to a remote server, if you allow it to use cookies, to run code, to inject a widget to pages and more. Unlike the world of minified external scripts, it is a framework that promotes transparency. Website owners can toggle permissions on and off, and if a Managed Component wasn’t granted a permission, it will not have access to the relevant API.</p><p>But Managed Components do more than wrapping the current system with permissions – they also provide functionality that was never available before: making server-side connections, caching information, using a key-value store, manipulating responses before they are handed to the browser and more. The core of this functionality comes from the ability to execute code outside the browser. Freeing the browser from running code that was previously executed in the browser, means that <a href=\"/cloudflare-acquires-zaraz-to-enable-cloud-loading-of-third-party-tools/\">your website can become approximately 40% faster</a>. It also results in a smaller attack surface in case your tool’s vendor gets hacked.</p><p>All of this is possible thanks to the new <a href=\"https://managedcomponents.dev/\">Managed Components API</a>. We designed it together with vendors, to make sure you can use them to write any tool, while keeping performance, security and privacy a top priority. At its core, a Managed Component is just a JavaScript module, and so every JavaScript developer should feel right at home when building one. Check out the <a href=\"/zaraz-open-source-managed-components-and-webcm/#introducing-managed-components\">two examples</a> in our previous blog post to see how they actually look like, or see some <a href=\"https://github.com/managed-components/\">Managed Components we already open sourced on GitHub</a>.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"webcm-is-the-open-source-component-manager\">WebCM is the open source Component Manager</h3>\n <a href=\"#webcm-is-the-open-source-component-manager\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>When tools are loaded with a <code>&lt;script&gt;</code> tag, their code is executed by the browser. Since Managed Components don’t run in the browser, their code needs to be executed somewhere else. This is the Component Manager. We designed the APIs around Managed Components deliberately to not be tied to a specific Component Manager, and in fact, there are already two in the world: Cloudflare Zaraz, and WebCM.</p><p><a href=\"http://webcm.dev/\">WebCM, Web-based Component Manager</a>, is our open source reference implementation of a Component Manager. If you run a website, you can use WebCM today to run Managed Components on your website, even if you’re not a Cloudflare user. If you want to create a Managed Component, you can use it like an SDK.</p><p>Over the last few months, we’ve been helping vendors to write their own Managed Components, and we will continue to do so. We open sourced WebCM to ensure that Managed Components are a technology of the Web as a whole. Everyone should be able to use WebCM to load and create Managed Components. Let’s see how to use it!</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"getting-started-with-webcm-in-5-minutes\">Getting started with WebCM in 5 minutes</h3>\n <a href=\"#getting-started-with-webcm-in-5-minutes\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Getting started with WebCM is easier than you think. Because WebCM works like a proxy, you can use it regardless of how your website is built. In a new folder, create a simple HTML file and call it <code>index.html</code>:</p>\n <pre class=\"language-html\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=”en”&gt;\n &lt;head&gt;\n\t&lt;meta charset=&quot;UTF-8&quot;&gt;\n\t&lt;title&gt;My Website&lt;/title&gt;\n &lt;/head&gt;\n &lt;body&gt;\n \t&lt;h1&gt;WebCM test website&lt;/h1&gt; \n &lt;/body&gt;\n&lt;/html&gt;</pre></code>\n <p>Let’s serve this file by launching an HTTP serve in the same folder:</p><p>You can use Node.js:</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">npx http-server -p 8000</pre></code>\n <p>You can use Python:</p>\n <pre class=\"language-bash\"><code class=\"language-bash\">python3 -m http.server</pre></code>\n <p>Or anything else that would serve our HTML file on <a href=\"http://localhost:8000/index.html\">http://localhost:8000/index.html</a>.</p><p>Next, create a configuration file for WebCM. In a new directory, create a file called <code>webcm.config.ts</code>.</p>\n <pre class=\"language-js\"><code class=\"language-js\">export default {\n components: [\n {\n name: &#039;demo&#039;,\n permissions: [\n &#039;access_client_kv&#039;,\n &#039;provide_server_functionality&#039;,\n &#039;provide_widget&#039;,\n &#039;serve_static_files&#039;,\n &#039;client_network_requests&#039;,\n ],\n },\n ],\n target: &#039;http://127.0.0.1:8000&#039;,\n hostname: &#039;localhost&#039;,\n trackPath: &#039;/webcm/track&#039;,\n ecommerceEventsPath: &#039;/webcm/ecommerce&#039;,\n clientEventsPath: &#039;/webcm/system&#039;,\n port: 8080\n}</pre></code>\n <p>Let’s go over this configuration file:</p><ul><li><p><code>components</code> is an array that lists all the Managed Components you want to load. For now, we will load the demo component. Note that all we needed was to specify “demo”, and WebCM will go and get it from NPM for us. Other Managed Components <a href=\"https://www.npmjs.com/org/managed-components\">are available on NPM</a> too, and you can install components from other sources too. For each component, we’re defining what <code>permissions</code> we are giving it. You can read more about the permissions in the <a href=\"https://managedcomponents.dev/specs/manifest/permissions\">specifications</a>. If we try to add the component without granting it its required permissions, WebCM will alert us.</p></li><li><p><code>target</code> is where our origin HTTP server runs. In the previous step, we set it to run on port 8000.</p></li><li><p><code>port</code> is the port under which WebCM will serve our website.</p></li><li><p><code>hostname</code> is the host WebCM will bind to.</p></li><li><p><code>trackPath</code>, <code>clientEventsPath</code>, <code>ecommerceEventsPath</code> are paths that WebCM will use to send events from the browser to its backend. We can leave these paths as they are for now, and will see how they’re used later.</p></li></ul><p>! Note: Node version 17 or higher is needed for the next section</p><p>While keeping your HTTP server running, and in the same directory as <code>webcm.config.ts</code>, run <code>npx webcm</code>. Node will fetch WebCM and start it for you, and WebCM will read the configuration. First, it will fetch the required components to a <code>components</code> folder. When done, it will start another server that proxies your origin.</p><p>If you open <a href=\"http://localhost:8080/index.html\">http://localhost:8080/index.html</a> in your browser now, you’d see the same page you saw at <a href=\"http://localhost:8000/index.html\">http://localhost:8000/index.html</a>. While the pages might look similar, the one running on port <code>8080</code> has our demo Managed Component running. Moving your mouse and clicking around the page should result in messages being printed in your WebCM terminal, showing that the component was loaded and that it is receiving data. You will also notice that the page now displays a simple weather widget - this a <a href=\"https://managedcomponents.dev/specs/embed-and-widgets/widgets\">Managed Component Widget</a> that got appended to your page. The weather information was fetched without the browser needing to contact any additional server, and WebCM can cache that information to make sure it is served fast. Lastly, if you go to <a href=\"http://localhost:8080/webcm/demo/cheese\">http://localhost:8080/webcm/demo/cheese</a>, you’ll see that the component is serving a static image of a cheese. This is an example of how Managed Components can expose new endpoints on your servers, if you allow them.</p><p>The Demo Component, like its name suggests, is just a demo. We use it to showcase and test the Managed Components APIs. What if we want to add a real Managed Component to our site? Google Analytics is <a href=\"https://w3techs.com/technologies/details/ta-googleanalytics\">used by more than half of the websites on the internet</a>, so let’s see how we edit our <code>webcm.config.ts</code> file to load it.</p>\n <pre class=\"language-js\"><code class=\"language-js\">export default {\n components: [\n {\n name: &#039;demo&#039;,\n permissions: [\n &#039;access_client_kv&#039;,\n &#039;provide_server_functionality&#039;,\n &#039;provide_widget&#039;,\n &#039;serve_static_files&#039;,\n &#039;client_network_requests&#039;,\n ],\n },\n {\n name: &#039;google-analytics&#039;,\n settings: { &#039;tid&#039;: &#039;UA-XXXXXX&#039; },\n permissions: [\n &#039;access_client_kv&#039;,\n ],\n },\n ],\n target: &#039;http://127.0.0.1:8000&#039;,\n hostname: &#039;localhost&#039;,\n trackPath: &#039;/webcm/track&#039;,\n ecommerceEventsPath: &#039;/webcm/ecommerce&#039;,\n clientEventsPath: &#039;/webcm/system&#039;,\n port: 8080\n}</pre></code>\n <p>In the above example, we just replaced our demo component with the Google Analytics Managed Component. Note that this component requires much fewer permissions to run - that’s because it is running 100% server-side. Remember to replace <code>UA-XXXXXX</code> with your real Google Universal Analytics (version 3) account identifier.</p><p>Re-running `npx webcm` will now fetch the <a href=\"https://www.npmjs.com/package/@managed-components/google-analytics\">google-analytics Managed Component</a> and run it, with the settings you provided. If you go now to your proxied website, you won’t see anything changed. But if you go to your Google Analytics dashboard, you will start seeing page views appearing on the Real Time view. WebCM loaded the component and is sending information server-side to Google Analytics.</p><p>There are many other components you can play around with, and we’re adding more all the time. Check out <a href=\"https://www.npmjs.com/org/managed-components\">the Managed Components organization on NPM</a> or <a href=\"https://github.com/managed-components\">on GitHub</a> to see the full list.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"build-your-own-managed-component\">Build your own Managed Component</h3>\n <a href=\"#build-your-own-managed-component\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Managed Components isn’t a closed library of tools you can use. As mentioned before – we are gradually open sourcing more tools from our library on GitHub. If you think our components are doing something weird – please let us know with an issue, or even make a PR. Managed Components are for the benefit of everyone. Over the past few months, the Cloudflare Zaraz community on Discord and on the Cloudflare Community Forum was extremely helpful in actively reporting issues, and so we’re excited to give them the option to take one step closer to the internals behind Cloudflare Zaraz.</p><p>While improving existing Managed Components is great, the thing we’re most thrilled about is that you can now build your own Managed Components too. If you’re a third-party tool vendor – this is a way for you to create a version of your tool that is safe and performant, so customers can discover and adopt your tool easily. If you’re a website developer, you might want to tinker with Managed Components to see what kind of things you can easily move away from the browser, for performance gains.</p><p>Let’s see how easy it is to create a Managed Component that listens to every page view on our website. Run <code>npm init managed-component</code> in the <code>components</code> directory that WebCM created, and <a href=\"https://github.com/managed-components/create-managed-component\">create-managed-component</a> will take you through the process of scaffolding your component files. To start with, our component will not use any special permissions, so you can select none.</p><p>Once we’re done with the wizard, we can open our <code>src/index.ts</code> file. By default, our component will add a listener to all page views:</p>\n <pre class=\"language-typescript\"><code class=\"language-typescript\">import { ComponentSettings, Manager } from &#039;@managed-components/types&#039;\n\nexport default async function (manager: Manager, settings: ComponentSettings) {\n manager.addEventListener(&#039;pageview&#039;, event =&gt; {\n // do the things\n })\n}</pre></code>\n <p>Let’s edit the comment line so that we can see whenever a page view happens. Note we also prefixed <code>settings</code> with a <code>_</code> because we’re not using it now:</p>\n <pre class=\"language-typescript\"><code class=\"language-typescript\">import { ComponentSettings, Manager } from &#039;@managed-components/types&#039;\n\nexport default async function (manager: Manager, _settings: ComponentSettings) {\n manager.addEventListener(&#039;pageview&#039;, event =&gt; {\n console.log(`New pageview at ${event.client.url}`)\n })\n}</pre></code>\n <p>With these changes, the component will print the current URL whenever a page is viewed on the website. Before trying it out, we need to build our component. In the folder of your component run <code>npm i &amp;&amp; npm run build</code>. Then, use the namespace of your component to add it to your webcm.config.ts file and restart WebCM:</p>\n <pre class=\"language-js\"><code class=\"language-js\">export default {\n components: [\n {\n name: &#039;demo&#039;,\n permissions: [\n &#039;access_client_kv&#039;,\n &#039;provide_server_functionality&#039;,\n &#039;provide_widget&#039;,\n &#039;serve_static_files&#039;,\n &#039;client_network_requests&#039;,\n ],\n },\n {\n name: &#039;google-analytics&#039;,\n settings: { &#039;tid&#039;: &#039;UA-123456&#039; },\n permissions: [\n &#039;access_client_kv&#039;,\n ],\n },\n {\n name: &#039;your-component-namespace&#039;,\n settings: {},\n permissions: [],\n },\n ],\n target: &#039;http://127.0.0.1:8000&#039;,\n hostname: &#039;localhost&#039;,\n trackPath: &#039;/webcm/track&#039;,\n ecommerceEventsPath: &#039;/webcm/ecommerce&#039;,\n clientEventsPath: &#039;/webcm/system&#039;,\n port: 8080\n}</pre></code>\n <p>This is a very simple component, but it shows how easy it is to build functionality that was previously only available in the browser. You can easily extend your component: use <code>fetch</code> next to the <code>console.log</code> statement and send information to your own analytics warehouse whenever a pageview happens on your site. Read about all the other Managed Components APIs to <a href=\"https://managedcomponents.dev/specs/category/embeds-and-widgets\">create widgets</a>, <a href=\"https://managedcomponents.dev/specs/client-events/mousedown\">listen to clicks</a>, <a href=\"https://managedcomponents.dev/specs/client/set\">store cookies</a>, <a href=\"https://managedcomponents.dev/specs/cache/useCache\">use cache</a>, and much more. These APIs allow you to build richer tools than it was ever possible before.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"your-tool-is-better-as-a-managed-component\">Your tool is better as a Managed Component</h3>\n <a href=\"#your-tool-is-better-as-a-managed-component\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>When we started working on Managed Components, many people were asking what would be the motivation of a tool vendor to build a Managed Component. During these last few months, we’ve learned that vendors are often excited about Managed Components for the same reasons we are - it provides a safe way to use their tools, and a streamlined way to integrate their tools in websites. Customers care deeply for these things, so having a Managed Component means that customers are more likely to try out your technology. Vendors will also get huge discoverability benefits, as their tools could be featured in the Cloudflare Zaraz dashboard, exposing them to tens of thousands of Zaraz-enabled websites. We are getting a lot of interest from major vendors in building a Managed Component, and we’re doing our best in actively supporting them in the process. If your company is interested in having a Managed Component, contact us.</p><p>We strongly believe that Managed Components can change the way third-party tools are used online. This is only the beginning of making them faster, secure and private. Together with users, and vendors, we will work on constantly improving the capabilities of Managed Components as a community, for the benefit of every user of the World Wide Web. To get started with building your Managed Component, head to <a href=\"http://managedcomponents.dev\">managedcomponents.dev</a> and start building. Our team is available to help you at <a href=\"mailto:managedcomponents@cloudflare.com\">managedcomponents@cloudflare.com</a>.</p>"],"published_at":[0,"2022-08-03T14:00:00.000+01:00"],"updated_at":[0,"2024-10-09T23:19:43.089Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6mTUjQxnnpCpSpy0SyQCH2/548d59090d0b7e31742cc602d84951b4/building-using-managed-components-webcm.png"],"tags":[1,[[0,{"id":[0,"5KsuE8WNLu5C4uyvr4XWCV"],"name":[0,"Zaraz"],"slug":[0,"zaraz"]}],[0,{"id":[0,"49e0LtPKRtCoIvqkN8MIde"],"name":[0,"Managed Components"],"slug":[0,"managed-components"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Yo'av Moshe"],"slug":[0,"yoav"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/26RcxowCLD305zrVRKtZES/91114909ce83a7d11280b2394a5f63b2/yoav.jpeg"],"location":[0,null],"website":[0,null],"twitter":[0,"@yoavmoshe"],"facebook":[0,null]}]]],"meta_description":[0,"This is how Managed Components can be useful for you right now, if you manage a website or if you’re building third-party tools."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Building and using Managed Components with WebCM Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"Translated for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/building-using-managed-components-webcm"],"metadata":[0,{"title":[0,"Building and using Managed Components with WebCM"],"description":[0,"This is how Managed Components can be useful for you right now, if you manage a website or if you’re building third-party tools."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7oDoM06sTmqZvw3rU14erV/a8a81ad9f97a86039288a723a7d92055/building-using-managed-components-webcm-7irfnp.png"]}]}],[0,{"id":[0,"24256QbRQUElgLBxTBRMfa"],"title":[0,"An Open-Source CMS on the Cloudflare Stack: Introductory Post"],"slug":[0,"production-saas-intro"],"excerpt":[0,"We are developing an example feature-complete SaaS application that will be built entirely on the Cloudflare stack."],"featured":[0,false],"html":[0,"\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/vFnWyzNerO5eQlVSkfymM/e44c3663b464e9f7ee25f3d82749b5b4/image3-31.png\" alt=\"An Open-Source CMS on the Cloudflare Stack: Introductory Post\" class=\"kg-image\" width=\"1200\" height=\"675\" loading=\"lazy\"/>\n \n </figure><p>The <a href=\"https://developers.cloudflare.com/\">Cloudflare documentation</a> is a great resource when learning concepts, reviewing API usage notes, or when you’re in need of a concise snippet to illustrate those APIs or concepts. But, as comprehensive as it is, new users to the Cloudflare Workers platform must bridge a large gap to go from the introductory example snippets to a real, production-ready application. While some of this may be specific to Workers (as with any platform), developers <i>everywhere</i> are figuring out how applications should be built in a serverless world. Building large serverless applications entails a learning curve journey, regardless of a developer’s experience level.</p><p>At Cloudflare, we’re intimately aware of this because we also had to go through the same transition. Our engineers are world-class and expertfully design and craft products that complement the distributed paradigm… but experts aren’t born overnight! We have been there, and we want to help jumpstart and aid others’ understanding.</p><p>With this in mind, we decided to do something unique to the industry: we are developing an example feature-complete SaaS application that will be built entirely on the Cloudflare stack. It is and will continue to be completely free, <a href=\"https://github.com/cloudflare/production-saas\">open-sourced on GitHub</a>, and developed in public. This will be an incredible time as it can be used as a template for launching your own SaaS applications, too! In fact, you can clone the GitHub repository, update a few service tokens, and deploy the pre-built application to your own Cloudflare account within minutes!</p><p>Of course, examples and templates are great, but technologies and best practices never stop evolving. Cloudflare is no exception and is constantly iterating and introducing new products and product features. By extension, this requires the SaaS application to be a <i>living example</i> that evolves alongside the Workers platform — and this is part of our commitment.</p><p>|| <b>Don’t miss out!</b> Watch the <a href=\"https://github.com/cloudflare/production-saas\">project on GitHub</a> to track its development progress and stay current with our latest changes and recommendations.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"application-overview\">Application Overview</h2>\n <a href=\"#application-overview\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Now, aside from actually building the application, we needed to find a balance between picking an example SaaS application that is both complex enough to serve as a convincing case study and simple — or self-contained — enough so that developers can quickly dive in, follow the source, and understand the components involved and the reasons why and/or how they are used.</p><p>Ultimately we decided to build an example content management system (CMS) which, as an application archetype, has also transformed over the years. Traditionally, a CMS operated on rented hardware, which was home to a long-lived server that handled incoming requests and queried an SQL-like database in order to retrieve the requested content, render it to an HTML page, and repeat the process over and over again. <a href=\"https://wordpress.org/\">WordPress</a> was — and still is — a very common example of this approach.</p><p>Naturally, this application architecture was improved over the years: layers of caching were introduced, database schemas were redesigned to minimize the number of rows processed, and some frameworks began to skip the database entirely, preferring a build-step to render all content upfront as static HTML pages. (This is now known as “static-site generation” and is still a very popular approach.)</p><p>Today, in the serverless era, there are a number of “headless CMS” options available. These are made “headless” because they are not monolithic web servers that render HTML for each request. Instead, they offer API endpoints that will return the content as raw JSON data. This allows web developers to build completely custom templates for their website using whatever tools and/or frameworks they prefer. This approach grants an enormous amount of flexibility to the developer without losing the ability to organize their content, image assets, etc. WordPress, the seasoned veteran, is one of few that is able to offer a headless <i>and</i> a “headful” mode. Other headless choices, like <a href=\"https://www.sanity.io/\">Sanity.io</a> and <a href=\"https://www.contentful.com/\">Contentful</a>, are also quite popular.</p><p>The CMS application model is a great case study for our open-source example. One of the primary tenets of an edge-first design is that content should be made available as close as physically possible to the users asking for it. And the serverless architecture means that there’s no longer — or should not be — a single point of failure. These both directly benefit the CMS archetype and, when implemented, will yield clear performance gains.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"current-progress\">Current Progress</h2>\n <a href=\"#current-progress\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Before diving into the roadmap and explaining how this project will progress, it’s important to call out that this project <i>has already been</i> — and will continue to be — an ongoing effort! Today, you can find the <a href=\"https://github.com/cloudflare/production-saas\">project on GitHub</a> and inspect the work that’s already been completed. As of now, the application already combines Workers, Workers KV, Cloudflare for SaaS, and Rate Limiting with Pages and Durable Objects additions to come in later milestones.</p><p><b>Phase 1</b> (see below) is nearing completion and, when finished, will mark the end of a very significant milestone. A new update to this blog post series will be issued, covering the highlights and technical overview of the project so far. This is important and immediately useful because <i>on its own,</i> <b>Phase 1</b> qualifies for a successful, full-stack application.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"development-milestones\">Development Milestones</h2>\n <a href=\"#development-milestones\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The CMS application will progress in milestones. We have already released the project and will continue to build upon it in accordance with the roadmap (below). <a href=\"https://github.com/cloudflare/production-saas\">GitHub stargazers</a> will be able to keep tabs on its progress, or at the very least, only subscribe to updates for the milestones they’re interested in following.</p><p>Each milestone is a sizable checkpoint on its own. As you’ll see below, the project roadmap is planned in a way such that each phase adds a considerable amount of new features and/or integrates with a new Cloudflare product. At every point, the application will remain functional and maintain a live, interactive demo to immediately demonstrate the latest functionality to passersby.</p><p>This format is chosen because it’s how real applications — and real products — are developed. Our goal is to ensure that the GitHub repository is <b>never</b> out of date. And, because of the development structure, one may <i>always</i> traverse the list of past milestones to review the changes that were necessary to migrate X or how product Y was integrated.</p><p><b>|| Note:</b> Visit the <a href=\"https://github.com/cloudflare/production-saas/milestones\">GitHub milestones</a> to view more details and to subscribe for updates. There is so much more than can be listed here.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"phase-1-json-api\">Phase 1 – JSON API</h3>\n <a href=\"#phase-1-json-api\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The project must begin with some API endpoints to start managing and manipulating data. Using Workers and Workers KV, the work within this milestone will focus on building a robust JSON API that handles the core functionality that the rest of the application will need.</p><p>There is no HTML, CSS, or client-side JavaScript involved in this phase. Instead, work here should focus solely on the data: how it’s accessed, how models relate to one another, and how best to structure and store these relationships within Workers KV. For example, individuals should be able to create and manage workspaces that belong to their personal user accounts or to the organization(s) that they belong to.</p><p>Additionally, when creating content, the document should be validated against an existing schema that the document was assigned. This feature is critical in any CMS platform that plans to handle thousands of documents within a workspace. Without it, there’s very little confidence that your contents’ JSON representation is consistently structured.</p><p>A number of other features are planned — subscription management and invoicing through <a href=\"https://stripe.com/\">Stripe</a>, sending transactional emails through <a href=\"https://sendgrid.com/\">SendGrid</a>, and assigning vanity domains to workspaces through <a href=\"/cloudflare-for-saas-for-all-now-generally-available/\">Cloudflare for SaaS</a>. Finally, of course, the standard house-keeping tasks will be set up. This includes continuous integration (CI) with API testing and automated, continuous deployments (CD).</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/50epXtqsrSebofS2ulnpsu/124d13127e4db7bd3fe160db00b563b0/image2-20.png\" alt=\"\" class=\"kg-image\" width=\"1156\" height=\"698\" loading=\"lazy\"/>\n \n </figure><p>By the end of this phase, the project will exist as a collection of API endpoints that, on its own, is a complete application. While it may only be accessible through <code>curl</code> commands — or any other preferred method for manually constructing HTTP requests, the completion of <b>Phase 1</b> already qualifies the project as a full-stack application and <i>could</i> serve a real-world SaaS product.</p><p>Additionally, the repository will include all the best practices for writing tests, automating deployments, and organizing the source in a way for long-term growth and maintenance. And, because we started with the JSON API, the project is immediately useful and capable of integrating with your existing build tools and frameworks. In other words, <a href=\"https://github.com/cloudflare/production-saas\">stargazers</a> could deploy the project to their own accounts as their own personalized Headless CMS. Perhaps some of you will build Gatsby or Eleventy plugins — if you do, please let us know!</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"phase-2-dashboard-ui\">Phase 2 — Dashboard UI</h3>\n <a href=\"#phase-2-dashboard-ui\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>As fun as <code>curl</code> may be, most people prefer some form of visual interface they can interact with. This phase will be all about assembling a frontend to serve as the CMS application’s dashboard.</p><p>We will use <a href=\"https://svelte.dev/\">Svelte</a>, a JavaScript framework for building user interfaces. While not everyone may enjoy or agree with this decision, the templating syntax resembles standard HTML markup, which will allow non-frontend developers to follow along and gauge what’s going on.</p><p>Svelte will be paired with <a href=\"https://tailwindcss.com/\">Tailwind CSS</a> for the design system. Tailwind is a very popular, utility-first CSS framework that allows developers to compose styles through predefined, reusable HTML <code>”class”</code> names.</p><p>The result will be a single-page application (SPA) and will be hosted on Cloudflare Pages. This means that, out of the box, the dashboard will be able to take advantage of <a href=\"https://developers.cloudflare.com/pages/platform/preview-deployments#customizing-preview-deployments-access\">Access-protected preview deployments</a>, <a href=\"https://developers.cloudflare.com/pages/platform/rollbacks\">instant rollbacks</a>, automated deployments, comprehensive analytics, and more.</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tr1PMuybglLESZjeCxqvS/621ceb9d180069a2d499c94d870257d4/image1-53.png\" alt=\"\" class=\"kg-image\" width=\"1156\" height=\"698\" loading=\"lazy\"/>\n \n </figure><p>Finally, <a href=\"/cloudflare-pages-goes-full-stack/\">now that Pages integrates with Workers directly</a>, the JSON API from <b>Phase 1</b> will migrate into a new repository structure. While this may seem like an innocent refactor, it actually unlocks an incredible set of features for the JSON API: <a href=\"https://developers.cloudflare.com/pages/platform/preview-deployments#customizing-preview-deployments-access\">Access-protected preview deployments</a>, <a href=\"https://developers.cloudflare.com/pages/platform/rollbacks\">instant rollbacks</a>, and automated deployments. Yes — these are the same Pages features mentioned above! This is amazing because it means that our API is continuously and atomically versioned, allowing its development to continue safely <i>alongside</i> the client dashboard that depends on it. In other words, there is zero risk of the API and the dashboard diverging, which would have allowed their expectations of one another to misalign. Instant rollbacks will also apply to the API since the <i>entire</i> application operates as a single Pages unit.</p><p>The previous phase will have built the core SaaS product functionality, but completing this phase will make it feel like a real-world product that can be launched and used on a daily basis. In fact, the end of <b>Phase 2</b> marks the application as a possible contender in the Headless CMS service space.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"phase-3-article-edge-rendering\">Phase 3 — Article Edge-Rendering</h3>\n <a href=\"#phase-3-article-edge-rendering\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The previous phases are focused on assembling a minimum viable Headless CMS product, but <b>Phase 3</b> looks to grow outside this archetypal box. This will happen by allowing the application to render HTML web pages by injecting the JSON content into predefined templates.</p><p>Like WordPress, the CMS application should allow its users to choose whether they want to continue using the “headless” feature or enlist the complete template engine. Should they opt for HTML output, the Cloudflare project will only include a few premade templates that a user may select from — but, of course, this can be customized in your own projects.</p><p>Even though this phase reintroduces the monolithic CMS archetype, it’s a significantly safer, faster, and more resilient architecture than the single, all-in-one server of yesteryear. The CMS contents will still be distributed around the world, close to the customers’ readers — but now, the content can be <i>rendered</i> from anywhere in the world at extremely low latencies, too.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"phase-4-feature-upgrades\">Phase 4 — Feature Upgrades</h3>\n <a href=\"#phase-4-feature-upgrades\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>At this stage, the application is — for the most part — complete. It’s functional, looks nice, performs well globally, and can be used in two very distinct ways.</p><p>In the context of a real SaaS product, development begins to shift towards adding new features that excite users or towards maintenance health of the project. For example, <b>Phase 4</b> will utilize <a href=\"/durable-objects-ga/\">Durable Objects</a> to introduce a document editor that allows multiple users to edit the same document in a real-time, collaborative environment.</p><p>It’s also very likely that <a href=\"/introducing-r2-object-storage/\">Cloudflare R2 Storage</a> will be introduced as a backend for media assets, allowing users to upload and manage images within a workspace. Or perhaps we decide to use <a href=\"/announcing-cloudflare-images/\">Cloudflare Images</a> for this and R2 is used for importing and exporting content backups.</p><p>As you may expect, this milestone is full of unknowns, but that’s because the future holds unlimited possibilities. The project will continue to evolve and expand with Cloudflare and with time.</p><p>Of course, if you have ideas or suggestions for features, start a discussion with us on GitHub. We would love to hear from you!</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"next-steps\">Next Steps</h2>\n <a href=\"#next-steps\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>This was the introductory post of (what will be) an ongoing series. When each milestone is completed, we will publish a new post in this series with a retrospective and with technical walkthroughs of key aspects from that chapter’s work.</p><p>We’re at the beginning of an exciting journey, and we hope you’re as interested as we are!</p><p>You can show your support by starring or <a href=\"https://github.com/cloudflare/production-saas\">following the project on GitHub</a>. All releases, discussions, and milestone tracking will reside within the repository. The next generation of SaaS applications will be built on Cloudflare — subscribe and dive in early!</p>"],"published_at":[0,"2021-11-19T13:59:24.000+00:00"],"updated_at":[0,"2024-10-09T23:16:17.123Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3FT0Odwog6j5oMG2RhDLEv/1034c61b25c7ecb6ab0b8f51224d11f7/production-saas-intro.png"],"tags":[1,[[0,{"id":[0,"4Q1oXZOskZczYetWpWXIsl"],"name":[0,"Full Stack Week"],"slug":[0,"full-stack-week"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"6hbkItfupogJP3aRDAq6v8"],"name":[0,"Cloudflare Workers"],"slug":[0,"workers"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}],[0,{"id":[0,"3JAY3z7p7An94s6ScuSQPf"],"name":[0,"Developer Platform"],"slug":[0,"developer-platform"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Luke Edwards"],"slug":[0,"lukeed"],"bio":[0,"Developer Advocate for Cloudflare Workers. Probably building an example, a library, or a half-finished framework. "],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JuHHFm6EOJc3LA6Odqu3F/238dfcf6b52fb8c5638db958612cc438/lukeed.jpg"],"location":[0,"San Francisco, CA"],"website":[0,"https://lukeed.com"],"twitter":[0,"@lukeed05"],"facebook":[0,null]}]]],"meta_description":[0,"We are developing an example feature-complete SaaS application that will be built entirely on the Cloudflare stack."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"An Open-Source CMS on the Cloudflare Stack: Introductory Post Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/production-saas-intro"],"metadata":[0,{"title":[0,"An Open-Source CMS on the Cloudflare Stack: Introductory Post"],"description":[0,"We are developing an example feature-complete SaaS application that will be built entirely on the Cloudflare stack."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/36DYCzlnvdAI4X4A9FqnNs/e82b92bcbc88ed9248f3d460edd6b751/production-saas-intro-0RpyW8.png"]}]}],[0,{"id":[0,"2YoV0y94C9jANAkQJ2V0TA"],"title":[0,"Migrating cdnjs to serverless with Workers KV"],"slug":[0,"migrating-cdnjs-to-serverless-with-workers-kv"],"excerpt":[0,"Cloudflare powers cdnjs, an open-source project that delivers popular JavaScript libraries to over 11% of websites. Today, we are excited to announce its migration to a serverless infrastructure using Cloudflare Workers and its distributed key-value store Workers KV!"],"featured":[0,false],"html":[0,"<p>Cloudflare powers <a href=\"https://cdnjs.com/\">cdnjs</a>, an open-source project that accelerates websites by delivering popular JavaScript libraries and resources via <a href=\"https://www.cloudflare.com/cdn/\">Cloudflare’s network</a>. Since <a href=\"/an-update-on-cdnjs/\">our major update in December</a>, we focused on remodelling cdnjs for scalability and resilience. Today, we are excited to announce how Cloudflare delivers cdnjs—a migration to a serverless infrastructure using <a href=\"https://developers.cloudflare.com/workers/\">Cloudflare Workers</a> and its distributed key-value store <a href=\"https://developers.cloudflare.com/workers/reference/storage\">Workers KV</a>!</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"what-is-cdnjs\">What is cdnjs?</h3>\n <a href=\"#what-is-cdnjs\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/K5YOmwNbwMpTE8ILtKovN/a242c00ce389b1e3365a3e8ffb062d01/imagesmall.png\" alt=\"\" class=\"kg-image\" width=\"1200\" height=\"357\" loading=\"lazy\"/>\n \n </figure><p>For those unfamiliar, cdnjs is an acronym describing a Content Delivery Network (CDN) for JavaScript (JS). A <a href=\"https://www.cloudflare.com/learning/cdn/what-is-a-cdn/\">CDN</a> simply refers to a geographically distributed network of servers that provide Internet content, whether it is memes, cat videos, or HTML pages. In our case, the CDN refers to Cloudflare’s ever <a href=\"/cloudflare-network-expands-to-more-than-100-countries/\">expanding network</a> of over 200 globally distributed data centers.</p><p>And here’s why this is relevant to you: it makes page load times lightning-fast. Virtually every website you visit needs to fetch JS libraries in order to load, including this one. Let’s say you visit a Sydney-based website that contains a local file from jQuery, a popular library found in <a href=\"https://w3techs.com/technologies/details/js-jquery\">76.2%</a> of websites. If you are located in New York, you may notice a delay, as it can easily exceed 300ms to fetch the file—not to mention the time it takes for the round trips involved with the <a href=\"/tls-1-3-overview-and-q-and-a/\">TLS handshake</a>. However, if the website references jQuery using <a href=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js\">cdnjs.cloudflare.com</a>, you can retrieve the file from the closest Cloudflare data center in Buffalo, reducing the latency to a blazing 20ms.</p><p>While cdnjs operates behind the scenes, it is used by <a href=\"https://w3techs.com/technologies/overview/content_delivery\">over 11%</a> of websites, making the Internet a much faster and more reliable place. In July, cdnjs served almost <a href=\"https://github.com/cdnjs/cf-stats/blob/master/2020/cdnjs_July_2020.md\">190 billion requests</a>—an enormous 3.46PB of data.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"where-are-the-files-stored\">Where are the files stored?</h3>\n <a href=\"#where-are-the-files-stored\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <!--kg-card-begin: html--><p><img style=\"border-width: 0px;\" src=\"/content/images/2020/09/image5-2.png\" alt=\"\" width=\"400\" height=\"333\" /></p><!--kg-card-end: html--><p>While cdnjs speeds up the Internet, it certainly isn’t magic!</p><p>Historically, a number of <a href=\"https://www.cloudflare.com/load-balancing/\">load-balanced</a> machines at one of Cloudflare’s core data centers would periodically pull cdnjs files from a backing store, acting as the origin for cdnjs.cloudflare.com. When a new file is requested, it is <a href=\"https://www.cloudflare.com/learning/cdn/what-is-caching/\">cached</a> by Cloudflare, allowing it to be fetched quickly from any of our data centers.</p><p>The backing store is a catalogue of JS, CSS, and other web libraries in the form of an open-source <a href=\"https://github.com/\">GitHub</a> repository. What this means is that anyone—including you—can contribute to it, subject to review and other processes.</p><p>However, until recently, these existing operations were very labor intensive and fragile.</p><p>This blog post will explain why we changed the infrastructure behind cdnjs to make it faster, more reliable, and easier to maintain. First, we will discuss how the community used to contribute to cdnjs, outlining the pains and concerns of the old system. Then, we will explore the benefits of migrating to Workers KV. After, we will dive into the new architecture, as well as upgrades to the website and cdnjs API. Finally, we will review the history of cdnjs, and where it is headed in the future.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"if-you-think-you-know-how-to-make-a-pr-think-again\">If you think you know how to make a PR, think again</h3>\n <a href=\"#if-you-think-you-know-how-to-make-a-pr-think-again\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Qz4kp8D2lbh2dSPTBQizG/1bcf42ec9c4b6f0553c38ce59666b707/image1-1.png\" alt=\"\" class=\"kg-image\" width=\"1577\" height=\"1418\" loading=\"lazy\"/>\n \n </figure><p>For the non-technical reader, a pull request (PR) is a request to merge changes you’ve made to a repository. Traditionally, if you wanted to include your JavaScript library in cdnjs, you would first create a PR on GitHub to <a href=\"https://github.com/cdnjs/cdnjs\">cdnjs/cdnjs</a> with a JSON file describing your package and additional files for any version you wished to include. Once your PR was approved by our <a href=\"https://github.com/PeterBot\">old bot</a>, manually reviewed, and then merged by a maintainer, your package would be integrated with cdnjs.</p><p>Sounds easy, right? You can just fork the repo, clone it, and copy paste a few files, no?</p><p>Exactly. Contributing was easy if you had several hours to burn, a case-sensitive file system, and a couple hundred gigabytes of free disk space to <a href=\"https://git-scm.com/docs/git-clone\">git clone</a> the 300GB repo. If you were short on time—no problem, you could always use your advanced knowledge of <a href=\"https://git-scm.com/docs/git-sparse-checkout\">git sparse-checkout</a> to get the job done. Don’t know git? Just add one file at a time manually through GitHub’s UI.</p><p>I think you get the point. I know I certainly did when I naively spent 10 hours cloning the repo, only to discover that macOS is case-insensitive by default.</p><p>However, updating cdnjs was not only difficult for the contributors, but also the maintainers. Historically, the community was able to contribute version files directly, which could potentially be malicious. This created lots of work for maintainers, requiring them to inspect each file manually, <a href=\"https://man7.org/linux/man-pages/man1/diff.1.html\">diffing</a> files against the official library source and running malware checks.So how did packages update once they were in cdnjs? In the JSON file describing each package, there was an optional auto-update definition telling the bot where to look for new versions of the library. If present, when your package released a new version from npm or GitHub, the bot would download it, pushing the files to <a href=\"https://github.com/cdnjs/cdnjs\">cdnjs/cdnjs</a> and computed <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity\">Subresource Integrity</a> (SRI) hashes to <a href=\"https://github.com/cdnjs/SRIs\">cdnjs/SRIs</a>. If the auto-update property was missing, it would be your responsibility to make manual PRs to update cdnjs with any future versions.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"a-wake-up-call-for-cdnjs\">A wake-up call for cdnjs</h3>\n <a href=\"#a-wake-up-call-for-cdnjs\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Bh8OS2VHZy5FKAySbhQns/84626f199211f856b3f230fcb109c161/image6-1-4.png\" alt=\"\" class=\"kg-image\" width=\"2000\" height=\"758\" loading=\"lazy\"/>\n \n </figure><p>In April, during maintenance at one of our core data centers, a technician accidentally disconnected the cables supplying all external connections to our other data centers, causing the data center to go offline for approximately four hours. <a href=\"/cloudflare-dashboard-and-api-outage-on-april-15-2020/\">This incident</a> served as the first wake-up call for cdnjs, especially since the affected data center housed the primary cdnjs origin web servers. In this case, we did have a backup running on an external provider, but what really saved us was Cloudflare’s global cache, which minimized the impact of the outage as only uncached assets failed to load.</p><p>We started to think about how we can improve both the reliability and performance of how we serve cdnjs. We went straight to <a href=\"https://workers.cloudflare.com/\">Cloudflare Workers</a>, our own platform for developing on the <a href=\"https://www.cloudflare.com/learning/cdn/glossary/edge-server/\">edge</a>. One powerful tool built into Workers is <a href=\"https://developers.cloudflare.com/workers/reference/storage\">Workers KV</a>—a low-latency, globally distributed key-value store optimized for high-read applications.</p><p>We put two and two together, realizing that instead of pulling the <a href=\"https://github.com/cdnjs/cdnjs\">cdnjs/cdnjs</a> repository and serving files from disk, we could cut the physical machines out entirely, distributing the data around the world and serving files straight from the edge. That way, cdnjs would be able to recover from any origin data center failure, while also increasing its scalability.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"workers-kv-to-the-rescue\">Workers KV to the rescue</h3>\n <a href=\"#workers-kv-to-the-rescue\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6dAu3V0R16Ugbf2Ad0nh4f/3ce544c6aa6c9558fdfbe686ff5ef640/image3-3.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"818\" loading=\"lazy\"/>\n \n </figure><p>At first glance, the decision to use Workers KV was a no-brainer. Since files in cdnjs never change but require frequent reads, Workers KV was a perfect fit.</p><p>However, as we planned our migration, we became concerned that with over 7 million assets in cdnjs, there would undoubtedly exist files that exceed <a href=\"https://developers.cloudflare.com/workers/about/limits/\">Workers KV’s 10MiB value limit</a>. After investigating, we discovered that several hundred cdnjs files were oversized, the majority being <a href=\"https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map\">JavaScript Source Maps</a>.</p><p>Then the idea hit us. We could store compressed versions of cdnjs files in Workers KV, not only solving our oversized file issue, but also optimizing how we serve files.</p><p>If you pay the Internet bill, you’ll know that <a href=\"/the-relative-cost-of-bandwidth-around-the-world/\">bandwidth is expensive</a>! For this reason, all modern browsers will try to <a href=\"/efficiently-compressing-dynamically-generated-53805/\">fetch compressed web content</a> whenever it is available. Similarly, within Cloudflare we often <a href=\"/results-experimenting-brotli/\">experiment with on-the-fly compression</a> to reduce our bandwidth, always serving compressed content to the eyeball when it is accepted. As a result, we decided to compress all cdnjs files ahead of time, writing them to Workers KV with both optimal <a href=\"https://github.com/google/brotli\">Brotli</a> and <a href=\"https://www.gzip.org/\">gzip</a> forms. That way, we could increase the compression level compared to on-the-fly compression as we no longer have the latency requirements.</p><p>This means we now serve cdnjs files faster and smaller!</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"a-complete-makeover-for-cdnjs\">A complete makeover for cdnjs</h3>\n <a href=\"#a-complete-makeover-for-cdnjs\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <!--kg-card-begin: html--><p><a href=\"/content/images/2020/09/image7-1.png\" target=\"_blank\" rel=\"noopener\"><img style=\"border-width: 0px;\" src=\"/content/images/2020/09/image7-1.png\" width=\"800\" height=\"498\" /></a></p><!--kg-card-end: html--><p>Today, if you want to include your JavaScript library in cdnjs, you first create a PR on GitHub to our new repository <a href=\"https://github.com/cdnjs/packages\">cdnjs/packages</a>. The repo is easily cloneable at 50MB and consists of thousands of JSON files, each describing a cdnjs package and how it is auto-updated from npm or git. Once your file is validated by our automated CI—powered by a <a href=\"https://github.com/cdnjs/tools\">new bot</a>—and merged by a maintainer, your package would be automatically enrolled in our auto-update service.</p><p>In the new system, security and maintainability are prioritized. For starters, cdnjs version files are created by our bot, minimizing the possibility of human error when merging a new version. While the JSON files in <a href=\"https://github.com/cdnjs/packages\">cdnjs/packages</a> are added by error-prone humans, they are inspected by our bot before being approved by a maintainer. Each file is automatically validated against a <a href=\"https://github.com/cdnjs/tools/blob/master/schema_human.json\">JSON schema</a>, as well as checked for popularity on npm or GitHub.</p><p>When the bot discovers a new release, it pushes Brotli and gzip-compressed versions of the files to a files namespace in Workers KV. With each entry, the bot writes some <a href=\"/catching-up-with-workers-kv/\">metadata in Workers KV</a> for the <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag\">ETag</a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified\">Last-Modified</a> HTTP headers. Similar to before, the bot also computes <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity\">Subresource Integrity</a> (SRI) hashes of the uncompressed files, but now pushes them instead to a SRIs namespace in Workers KV.</p><p>Then, when a new file is requested from cdnjs.cloudflare.com, a <a href=\"https://developers.cloudflare.com/workers/\">Cloudflare Worker</a> will inspect the client’s <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding\">Accept-Encoding</a> header, fetching either the Brotli or gzip-compressed version with its ETag and Last-Modified metadata from Workers KV. As the compressed file travels back through Cloudflare, it is cached for future requests and uncompressed on-the-fly if needed.</p><p>At the moment, there are still a handful of files exceeding Workers KV’s size limit. Consequently, if the Cloudflare Worker fails to retrieve a file from Workers KV, it is fetched from the origin backed by the original git repo. In the coming months, we plan on gradually removing this infrastructure.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"scaling-the-website-and-api\">Scaling the website and API</h3>\n <a href=\"#scaling-the-website-and-api\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n \n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2q1lBMUDzWZUSCs9rMRTIw/a0b9baf89cd1056752a84566fe31dda8/image2.gif\" alt=\"\" class=\"kg-image\" width=\"800\" height=\"266\" loading=\"lazy\"/>\n \n </figure><p>Besides the core cdnjs infrastructure, many of its other components received upgrades as well!</p><p>On the cdnjs project’s <a href=\"https://cdnjs.com/\">homepage</a>, you will be greeted by a slick <a href=\"https://github.com/cdnjs/static-website\">new beta website</a> built by <a href=\"https://github.com/mattipv4\">Matt</a>. Constructed with <a href=\"https://vuejs.org/\">Vue</a> and <a href=\"https://nuxtjs.org/\">Nuxt</a>, the beta website is powered entirely by the <a href=\"https://cdnjs.com/api\">cdnjs API</a>. As a result, it is always up-to-date with the latest package information and requires low resource usage to serve the site—which runs completely on the client-side after the first page load—helping us scale with cdnjs’s never-ending growth.</p><p>In fact, the cdnjs API also strengthened its scalability, benefitting from a serverless architecture close to the one we have seen with cdnjs and Workers KV.</p><p>Before migrating to Workers KV, the cdnjs API relied on a regularly scheduled process that involved generating about 300MB of metadata. The cdnjs API’s backend would then fetch this enormous “package.min.js” file into memory and use it to operate the API. Similarly, file SRIs were pushed to <a href=\"https://github.com/cdnjs/SRIs\">cdnjs/SRIs</a>, which was cloned by the API locally to serve SRI responses.</p><p>After all cdnjs files (within the permitted size limit) were moved to Workers KV, these legacy processes became unsustainable, requiring millions of reads and an unreasonable amount of time. Therefore, we decided to upload all metadata found into Workers KV. We split the metadata into four namespaces—one for package-level metadata, one for version-specific metadata, one containing aggregated metadata, and one for file SRIs.</p><p>Similar to cdnjs’s serverless design, a Cloudflare Worker sits on top of <a href=\"http://metadata.speedcdnjs.com/packages\">metadata.speedcdnjs.com</a>, serving data from Workers KV using several public endpoints. Currently, the cdnjs API is fully integrated with these endpoints, which provide an elegant solution as cdnjs continues to scale.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"transparency-and-the-future-of-cdnjs\">Transparency and the future of cdnjs</h3>\n <a href=\"#transparency-and-the-future-of-cdnjs\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Since its birth in January 2011, cdnjs has always been deeply rooted in transparency, deriving its strength from the community. Even when cdnjs exploded in size and its founders Ryan Kirkman and Thomas Davis <a href=\"/cdnjs-community-moderated-javascript-librarie/\">teamed up with us in June 2011</a>, the project remained entirely open-source on <a href=\"https://github.com/cdnjs/\">GitHub</a>.</p><p>As the years passed, it became harder for the founders to stay active, heavily depending on the community for support. With a nearly nonexistent budget and little access to the repository, core cdnjs maintainers were challenged every day to keep the project alive.</p><p>Last year, this led us to contact the founders, <a href=\"https://news.ycombinator.com/item?id=21416614\">who were happy to have our assistance with the project</a>. With Cloudflare’s increased role, cdnjs is as stable as ever, with <a href=\"https://cdnjs.com/about\">active members</a> from both Cloudflare and the community.</p><p>However, as we remove our reliance on the legacy system and store files in Workers KV, there are concerns that cdnjs will become proprietary. Don’t worry, we are working hard to ensure that cdnjs remains as transparent and open-source as possible. To help the community audit updates to Workers KV, there is a new repository, <a href=\"https://github.com/cdnjs/logs\">cdnjs/logs</a>, which is used by the bot to log all Workers KV-related events. Furthermore, anyone can validate the integrity of cdnjs files by fetching SRIs from the cdnjs API.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"conclusion\">Conclusion</h3>\n <a href=\"#conclusion\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Overall, this past year has been a turbulent time for cdnjs, but all of its shortcomings have acted as red flags to help us build a better system. Most recently, we have mitigated the risks of depending on physical machines at a single location, migrating cdnjs to a serverless infrastructure where its files are stored in <a href=\"https://developers.cloudflare.com/workers/reference/storage\">Workers KV</a>.</p><p>Today, cdnjs is in good hands, and is not going away anytime soon. Shout out especially to the maintainers <a href=\"https://github.com/xtuc\">Sven</a> and <a href=\"https://github.com/mattipv4\">Matt</a> for creating tons of momentum with the project, working on everything from scaling cdnjs to editing this post.</p><p>Moving forward, we are committed to making cdnjs as transparent as possible. As we continue to improve cdnjs, we will release more blog posts to keep the community up to date. If you are interested, please subscribe to our blog. After all, it is the community that makes cdnjs possible! A special thanks to our active GitHub contributors and members of the <a href=\"https://github.com/cdnjs/cdnjs/discussions/\">cdnjs Community Forum</a> for sticking with us!</p>"],"published_at":[0,"2020-09-10T12:00:00.000+01:00"],"updated_at":[0,"2024-10-09T23:11:54.549Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2KT7mX75POQq6wvpmqAbAy/b2489955f86a39fec4ea0a27ad6f7a7b/migrating-cdnjs-to-serverless-with-workers-kv.png"],"tags":[1,[[0,{"id":[0,"1goKAxv27P7ytwqLodJSAK"],"name":[0,"CDNJS"],"slug":[0,"cdnjs"]}],[0,{"id":[0,"q88b6J14cYlPx98w6u1If"],"name":[0,"Cloudflare Workers KV"],"slug":[0,"cloudflare-workers-kv"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"48r7QV00gLMWOIcM1CSDRy"],"name":[0,"Speed & Reliability"],"slug":[0,"speed-and-reliability"]}],[0,{"id":[0,"5cye1Bh5KxFh3pKSnX8Dsy"],"name":[0,"Serverless"],"slug":[0,"serverless"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Tyler Caslin"],"slug":[0,"tyler"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7D6hUG1uj4Ig9zgf5uXILB/4192f31545ed36b38ef74c124e757a73/tyler.JPG"],"location":[0,null],"website":[0,"https://github.com/tc80"],"twitter":[0,"@tycaslin"],"facebook":[0,null]}]]],"meta_description":[0,"cdnjs, an open-source project that delivers popular JavaScript libraries to over 11% of websites has been migrated to a serverless infrastructure, using Cloudflare Workers and its distributed key-value store Workers KV!"],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Migrating cdnjs to serverless with Workers KV Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"Translated for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"Translated for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/migrating-cdnjs-to-serverless-with-workers-kv"],"metadata":[0,{"title":[0,"Migrating cdnjs to serverless with Workers KV"],"description":[0,"cdnjs, an open-source project that delivers popular JavaScript libraries to over 11% of websites has been migrated to a serverless infrastructure, using Cloudflare Workers and its distributed key-value store Workers KV!"],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Xp4CRImILejIdelrheZ56/2caeab9ed10fd8ced59000cfccdb84f8/migrating-cdnjs-to-serverless-with-workers-kv-qRIBqX.png"]}]}],[0,{"id":[0,"6GUF8P2EMzt01zbVqpBOwx"],"title":[0,"An Update on CDNJS"],"slug":[0,"an-update-on-cdnjs"],"excerpt":[0,"CDNJS powers over 10% of the web. A month ago it needed our help, now it needs yours."],"featured":[0,false],"html":[0,"<p>When you loaded this blog, a file was delivered to your browser called jquery-3.2.1.min.js. jQuery is a library which makes it easier to build websites, and was at one point included on as many as <a href=\"https://w3techs.com/technologies/details/js-jquery\">74.1%</a> of all websites. A full eighteen million sites include jQuery and other libraries using one of the most popular tools on Earth: <a href=\"https://cdnjs.com/\">CDNJS</a>. Beginning about a month ago Cloudflare began to take a more active role in the operation of CDNJS. This post is here to tell you more about CDNJS’ history and explain why we are helping to manage CDNJS.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"what-cdnjs-does\">What CDNJS Does</h2>\n <a href=\"#what-cdnjs-does\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Virtually every site is composed of not just the code written by its developers, but also dozens or hundreds of libraries. These libraries make it possible for websites to extend what a web browser can do on its own. For example, libraries can allow a site to include powerful <a href=\"https://d3js.org/\">data visualizations</a>, respond to <a href=\"https://www.meteor.com/\">user input</a>, or even <a href=\"https://instant.page/\">get more performant</a>.</p><p>These libraries created wondrous and magical new capabilities for web browsers, but they can also cause the size of a site to explode. Particularly a decade ago, connections were not always fast enough to permit the use of many libraries while maintaining performance. But if so many websites are all including the same libraries, why was it necessary for each of them to load their own copy?</p><p>If we all load jQuery from the same place the browser can do a much better job of not actually needing to download it for every site. When the user visits the first jQuery-powered site it will have to be downloaded, but it will already be cached on the user&#39;s computer for any subsequent jQuery-powered site they might visit.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5KvbGCC3AOaU9YfFjRp4Ji/707e8cbacf87ff72fdd26bd197b7f925/2-1.png\" alt=\"\" class=\"kg-image\" width=\"1342\" height=\"921\" loading=\"lazy\"/>\n \n </figure><p>The first visit might take time to load:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/zjCDXJufSaYaIhnivnaju/956ee4881f53e4be9f49871b4ba1fb73/Screen-Shot-2019-12-17-at-2.25.49-PM.png\" alt=\"\" class=\"kg-image\" width=\"2206\" height=\"106\" loading=\"lazy\"/>\n \n </figure><p>But any future visit to any website pointing to this common URL would already be cached:</p>\n <figure class=\"kg-card kg-image-card \">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3NSQiCFdy2m4kJkHkARFoh/a86141bb665b85ca46d1d77cd95e35ad/Screen-Shot-2019-12-17-at-2.26.09-PM.png\" alt=\"\" class=\"kg-image\" width=\"2000\" height=\"86\" loading=\"lazy\"/>\n \n </figure>\n <pre class=\"language-html\"><code class=\"language-html\">&lt;!-- Loaded only on my site, will need to be downloaded by every user --&gt;\n&lt;script src=&quot;./jquery.js&quot;&gt;&lt;/script&gt;\n\n&lt;!-- Loaded from a common location across many sites --&gt;\n&lt;script src=&quot;https://cdnjs.cloudflare.com/jquery.js&quot;&gt;&lt;/script&gt;</pre></code>\n <p>Beyond the performance advantage, including files this way also made it very easy for users to experiment and create. When using a web browser as a creation tool users often didn&#39;t have elaborate build systems (this was also before <a href=\"https://www.npmjs.com/\">npm</a>), so being able to include a simple script tag was a boon. It&#39;s worth noting that it&#39;s not clear a massive performance advantage was ever actually provided by this scheme. It is becoming even less of a performance advantage now that browser vendors are beginning to use separate cache&#39;s for each website you visit, but with millions of sites using CDNJS there&#39;s no doubt it is a critical part of the web.</p><h1>A CDN for all of us</h1><p>My first <a href=\"https://github.com/cdnjs/cdnjs/pull/1369\">Pull Request</a> into the CDNJS project was in 2013. Back then if you created a JavaScript project it wasn&#39;t possible to have it included in the jQuery CDN, or the ones provided by large companies like Google and Microsoft. They were only for big, important, projects. Of course, however, even the biggest project starts small. The community needed a <a href=\"https://www.cloudflare.com/learning/cdn/what-is-a-cdn/\">CDN</a> which would agree to host nearly all JavaScript projects, even the ones which weren&#39;t world-changing (yet). In 2011, that project was launched by Ryan Kirkman and Thomas Davis as <a href=\"https://cdnjs.com\">CDNJS</a>.</p><p>The project was quickly wildly successful, far beyond their expectations. Their CDN bill quickly began to skyrocket (it would now be over a million dollars a year on AWS). Under the threat of having to shut down the service, Cloudflare was approached by the CDNJS team to see if we could help. We agreed to support their efforts and created cdnjs.cloudflare.com which serves the CDNJS project free of charge.</p><p>CDNJS has been astonishingly successful. The project is currently installed on over eighteen million websites (<a href=\"https://w3techs.com/technologies/overview/content_delivery\">10%</a> of the Internet!), offers files totaling over <a href=\"https://cdnjs.com/git_stats/cdnjs/general.html\">1.5 billion lines of code</a>, and serves over 173 billion requests a month. CDNJS only gets more popular as sites get larger, with <a href=\"https://trends.builtwith.com/cdn/CDN-JS\">34%</a> of the top 10k websites using the service. Each month we serve almost three petabytes of JavaScript, CSS, and other resources which power the web via cdnjs.cloudflare.com.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7nE6O3729XDte6o95NNThA/b91592cf9679b14f5b4aef57db8afe86/pasted-image-0-1.png\" alt=\"\" class=\"kg-image\" width=\"1190\" height=\"724\" loading=\"lazy\"/>\n \n </figure><p>Spikes can happen when a very large or popular site installs CDNJS, or when a misbehaving web crawler discovers a CDNJS link.</p><p>The future value of CDNJS is now in doubt, as web browsers are beginning to use a <a href=\"https://www.chromestatus.com/feature/5730772021411840\">separate cache</a> for every website you visit. It is currently used on such a <a href=\"https://w3techs.com/technologies/overview/content_delivery\">wide swath of the web</a>, however, it is unlikely it will be disappearing any time soon.</p>\n <div class=\"flex anchor relative\">\n <h2 id=\"how-cdnjs-works\">How CDNJS Works</h2>\n <a href=\"#how-cdnjs-works\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>CDNJS starts with a Github repo. That project contains every file served by CDNJS, at every version which it has ever offered. That’s 182 GB without the commit history, over five million files, and over 1.5 billion lines of code.</p><p>Given that it stores and delivers versioned code files, in many ways it was the Internet’s first JavaScript package manager. Unlike other package managers and even other CDNs everything CDNJS serves is publicly versioned. All 67,724 commits! This means you as a user can verify that you are being served files which haven’t been tampered with.</p><p>To make changes to CDNJS a commit has to be made. For new projects being added to CDNJS, or when projects change significantly, these commits are made by humans, and get reviewed by other humans. When projects just release new versions there is a bot made by <a href=\"https://twitter.com/PeterDaveHello\">Peter</a> and maintained by <a href=\"http://github.com/xtuc\">Sven</a> which sucks up changes from npm and automatically creates commits.</p><p>Within Cloudflare’s infrastructure there is a set of machines which are responsible for pulling the latest version of the repo periodically. Those machines then become the origin for cdnjs.cloudflare.com, with <a href=\"https://www.cloudflare.com/load-balancing/\">Cloudflare’s Global Load Balancer</a> automatically handling failures. Cloudflare’s cache automatically stores copies of many of the projects making it possible for us to deliver them quickly from all 195 of our data centers.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/y6zjzefb5QVkla0fcCbUA/029fedc23230e957333de2c912df2113/1-with-arrow-v2-1.png\" alt=\"\" class=\"kg-image\" width=\"1450\" height=\"1014\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h2 id=\"the-internet-on-a-shoestring-budget\">The Internet on a Shoestring Budget</h2>\n <a href=\"#the-internet-on-a-shoestring-budget\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>The CDNJS project has always been administered independently of Cloudflare. In addition to the founders, the project has additionally been maintained by exceptionally hard-working caretakers like <a href=\"https://twitter.com/PeterDaveHello\">Peter</a> and <a href=\"https://twitter.com/MattIPv4\">Matt Cowley</a>. Maintaining a single repo of nearly every frontend project on Earth is no small task, and it has required a substantial amount of both manual work and bot development.</p><p>Unfortunately approximately thirty days ago one of those bots stopped working, preventing updated projects from appearing in CDNJS. The bot&#39;s open-source maintainer was not able to invest the time necessary to keep the bot running. After several weeks we were asked by the community and the CDNJS founders to take over maintenance of the CDNJS repo itself. This means the Cloudflare engineering team is taking responsibility for keeping the contents of <a href=\"https://github.com/cdnjs/cdnjs\">github.com/cdnjs/cdnjs</a> up to date, in addition to ensuring it is correctly served on cdnjs.cloudflare.com.</p><p>We agreed to do this because we were, frankly, scared. Like so many open-source projects CDNJS was a critical part of our world, but wasn’t getting the attention it needed to survive. The Internet relies on CDNJS as much as on any other single project, losing it or allowing it to be commandeered would be catastrophic to millions of websites and their visitors. If it began to fail, some sites would adapt and update, others would be broken forever.</p><p>CDNJS has always been, and remains, a project for and by the community. We are invested in making all decisions in a transparent and inclusive manner. If you are interested in contributing to CDNJS or in the topics we&#39;re currently discussing please visit the <a href=\"https://github.com/cdnjs/cdnjs/issues\">CDNJS Github Issues</a> page.</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1RYsRv22prgSJsOzeUTFMG/97b5dec8a319da206c50d43cb78dbd72/pasted-image-0-1-1.png\" alt=\"\" class=\"kg-image\" width=\"1600\" height=\"254\" loading=\"lazy\"/>\n \n </figure>\n <div class=\"flex anchor relative\">\n <h2 id=\"a-plan-for-the-future\">A Plan for the Future</h2>\n <a href=\"#a-plan-for-the-future\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>One example of an area where we could use your help is in charting a path towards a CDNJS which requires less manual moderation. Nothing can replace the intelligence and creativity of a human (yet), but for a task like managing what resources go into a CDN, it is error prone and time consuming. At present a human has to review every new project to be included, and often has to take additional steps to include new versions of a project.</p><p>As a part of our analysis of the project we examined a snapshot of the still-open PRs made against CDNJS for several months:</p>\n <figure class=\"kg-card kg-image-card kg-width-wide\">\n \n <Image src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ffwfsahT0occRgh467Exy/c4441f487765636a0e05a36feebdb375/imageLikeEmbed.png\" alt=\"\" class=\"kg-image\" width=\"1543\" height=\"954\" loading=\"lazy\"/>\n \n </figure><p>The vast majority of these PRs were changes which ultimately passed the automated review but nevertheless couldn&#39;t be merged without manual review.</p><p>There is consensus that we should move to a model which does not require human involvement in most cases. We would love your input and collaboration on the best way for that to be solved. If this is something you are passionate about, please <a href=\"https://github.com/cdnjs/cdnjs/issues/13613\">contribute here</a>.</p><p>Our plan is to support the CDNJS project in whichever ways it requires for as long as the Internet relies upon it. We invite you to use CDNJS in your next project with the full assurance that it is backed by the same network and team who protect and accelerate over twenty million of your favorite websites across the Internet. We are also planning more posts diving further into the CDNJS data, subscribe to this blog if you would like to be notified upon their release.</p>"],"published_at":[0,"2019-12-19T19:30:19.000+00:00"],"updated_at":[0,"2024-10-10T00:31:21.348Z"],"feature_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5kKcwNwDcT58jsT5iQXCr9/d64390fb7d94a9156120390b2a110471/an-update-on-cdnjs.png"],"tags":[1,[[0,{"id":[0,"1goKAxv27P7ytwqLodJSAK"],"name":[0,"CDNJS"],"slug":[0,"cdnjs"]}],[0,{"id":[0,"78aSAeMjGNmCuetQ7B4OgU"],"name":[0,"JavaScript"],"slug":[0,"javascript"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}],[0,{"id":[0,"4HIPcb68qM0e26fIxyfzwQ"],"name":[0,"Developers"],"slug":[0,"developers"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"Zack Bloom"],"slug":[0,"zack-bloom"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ptAECKtpn049gpOBg7rzq/ab43c5311955486d8ee097bfa948b26a/zack-bloom.jpg"],"location":[0,null],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,"CDNJS powers over 10% of the web. A month ago it needed our help, now it needs yours."],"primary_author":[0,{}],"localeList":[0,{"name":[0,"An Update on CDNJS Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/an-update-on-cdnjs"],"metadata":[0,{"title":[0,"An Update on CDNJS"],"description":[0,"Beginning about a month ago Cloudflare began to take a more active role in the operation of CDNJS. This post is here to tell you more about CDNJS’ history and explain why we are helping to manage CDNJS."],"imgPreview":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7vay8R4XCQ4lZVtICsG1Yg/0c8dbbea457a0695495070f89145ddbb/an-update-on-cdnjs-3AJxEZ.png"]}]}],[0,{"id":[0,"66ZqFVMh8TtYeR1YgLGm4C"],"title":[0,"Helping To Build Cloudflare, Part 4: Public Engagement"],"slug":[0,"helping-to-build-cloudflare-part-4"],"excerpt":[0,"We don’t believe that any of our software, not a single line of code, provides us with a long-term advantage. We could, today, open source every single line of code at Cloudflare and we don’t believe we’d be hurt by it."],"featured":[0,false],"html":[0,"<p>This is part 4 of a six part series based on a talk I gave in Trento, Italy. To start from the beginning go <a href=\"/helping-to-build-cloudflare-part-1\">here</a>.</p><p>We don’t believe that any of our software, not a single line of code, provides us with a long-term advantage. We could, today, open source every single line of code at Cloudflare and we don’t believe we’d be hurt by it.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"how-we-think-about-open-source\">How we think about Open Source</h3>\n <a href=\"#how-we-think-about-open-source\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>Why don’t we? We actually do <a href=\"https://cloudflare.github.io/\">open source a lot of code</a>, but we try to be thoughtful about it. Firstly, a lot of our code is so Cloudflare-specific, full of logic about how our service works, that it’s not generic enough for someone else to pick up and use for their service. So, for example, open sourcing the code that runs our web front end would be largely useless.</p><p>But other bits of software are generic. There’s currently a debate going on internally about a piece of software called Quicksilver. I mentioned before that Cloudflare used a distributed key-value store to send configuration to machines across the world. We used to use an open source project called Kyoto Tycoon. It was pretty cool.</p><p>But it ended up not scaling to our size. It was great when we had a small number of locations worldwide, but we ran into operational problems with 100s of locations. And it wasn’t, by default, secure and so we had to add security to it. Once we did, we open sourced that change, but at some point when using open source software you have to make a “modify or rewrite” decision.</p><p>We’d done that in the past with PowerDNS. Originally our DNS service was based on PowerDNS. And it was great. But as we scaled, we ran into problems fitting it into our system. Not because there’s something wrong with PowerDNS but because we have a lot of DNS-related logic and we were shoehorning things into PowerDNS, and it was getting less and less maintainable for us. This was not PowerDNS&#39; fault; we&#39;d built such a large edifice of business logic around it that PowerDNS was being crushed by the sheer weight of that logic: it made sense to start over and integrate logic and DNS into a single code base.</p><p>Eventually we wrote our own server, RRDNS, in Go, that is now the code behind the largest and fastest authoritative DNS service on the planet. That’s another piece of software we haven’t open sourced. That one because it’s riddled with business logic and handling of special conditions (like the unique challenges of working inside China).</p><p>But back to Quicksilver. It’s based on LMDB and does all data and code sync. across our global network. Typically, a change (you click a button in our UI or you upload code for our edge compute product) and it’s distributed globally in 5s. That’s cool.</p><p>And Quicksilver is generic. It doesn’t contain lots of Cloudflare-specific logic and it’s likely useful for others. The internal debate is about whether we have time to nurture and handle the community that would grow up around Quicksilver. You may recently have seen the creator of Ruby saying on Twitter “We are mere mortals” pointing out that the people behind popular open source projects only have so much time. And we take a lesson from the creators of Kyoto Tycoon who have now largely abandoned it to do other things.</p><p>Perhaps Quicksilver will get open sourced this year, we’ll see. But our rule for open sourcing is: “Is this something others can use and is this something we have time to maintain in public?”. Of course, where we modify existing open source software, we upstream everything we can. Inevitably, some projects don’t accept our PRs and so we have to maintain internal forks.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"how-we-think-about-patents\">How we think about Patents</h3>\n <a href=\"#how-we-think-about-patents\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>While we’re on the topic of intellectual property let’s talk about patents. Cloudflare has a lot of patents. Although it might be nice to live in a world where there were no software patents it’s a little like nuclear weapons. It’s very hard for one country to disarm unilaterally because a power imbalance is left behind. If Cloudflare didn’t patent aspects of our software, then others would and would then use them against us.</p><p>So, we patent for defensive reasons. To stop others from using the patent system against us.</p>\n <div class=\"flex anchor relative\">\n <h3 id=\"working-with-governments\">Working With Governments</h3>\n <a href=\"#working-with-governments\" aria-hidden=\"true\" class=\"relative sm:absolute sm:-left-5\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"><path fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\"></path></svg>\n </a>\n </div>\n <p>And speaking of patents let’s talk about governments. Lots of technology companies think they are too cool for school. They don’t need to think about governments because technology moves faster than them and what do those old, boring lawmakers know about anything anyway?</p><p>Wrong. Dead wrong.</p><p>Yes, governments move slowly. You actually want them to. Imagine if governments changed policies as fast as chat apps get launched. It would be a nightmare. But just because they are slow, they can’t be ignored.</p><p>Put simply governments have tanks and you don’t. Eventually lawmakers will make laws that affect you and unless you’ve spent time explaining to them what it is you do you may have a nasty surprise. </p><p>Cloudflare decided very early on to engage with lawmakers in the US and Europe. We did this by helping them understand what is happening in the Internet, what challenges we foresee, and helping them with the technical arcana that we all deal with.</p><p>If there’s any chance that your business ends up being regulated by a government then you should engage early. Cloudflare thinks a lot about things like copyright law, the fight against online extremism, and privacy. We have to because our network is used by 13 million web sites and services and all manner of things pass through it.</p><p>Lots of times people get mad at us because they don’t like a particular customer on our network. This is tough for us because oftentimes we don’t like them either. But here’s the tricky thing: do you really want me, or Matthew, deciding what’s online? Because many times that’s what angry mobs are asking.</p><p>“Shut this down”, “Throw this off your service”. It’s odd that people are asking that corporations be gatekeepers when corporations answer to shareholders and not the people. The right answer is that if you see something you don’t like online: engage in the political process in your country.</p><p>The transparency of democratic institutions and, in particular, the judiciary is vital to the long-term survival of countries. It’s through those institutions that people need to express their desire for what’s allowed and not allowed. </p><p>How do you engage with governments? Every single government has committees and advisory bodies that are dying to have people from industry help out. Go find the bodies that are doing work that overlaps with your company, don’t be put off by how old-fashioned they seem, and get involved.</p><p><a href=\"/helping-to-build-cloudflare-part-5/\">On to part 5</a>.</p><hr/><h4>Helping to Build Cloudflare</h4><ul><li><p><a href=\"/helping-to-build-cloudflare-part-1/\">Part 1: How I came to work here</a></p></li><li><p><a href=\"/helping-to-build-cloudflare-part-2/\">Part 2: The Most Difficult Fortnight</a></p></li><li><p><a href=\"/helping-to-build-cloudflare-part-3/\">Part 3: Audacity, Diversity and Change</a></p></li><li><p>Part 4: Public Engagement (you are here)</p></li><li><p><a href=\"/helping-to-build-cloudflare-part-5/\">Part 5: People: Finding, Nurturing and Learning to Let Go</a></p></li><li><p><a href=\"/helping-to-build-cloudflare-part-6/\">Part 6: What does Cloudflare&#39;s CTO do?</a></p></li></ul>"],"published_at":[0,"2019-02-04T08:41:00.000+00:00"],"updated_at":[0,"2024-10-10T00:43:17.652Z"],"feature_image":[0,"https:undefined"],"tags":[1,[[0,{"id":[0,"4g8tPriKOAUwdUT4jNPebe"],"name":[0,"Life at Cloudflare"],"slug":[0,"life-at-cloudflare"]}],[0,{"id":[0,"73tAN5Lulrx0pDbXqrBcqM"],"name":[0,"Cloudflare History"],"slug":[0,"cloudflare-history"]}],[0,{"id":[0,"3txfsA7N73yBL9g3VPBLL0"],"name":[0,"Open Source"],"slug":[0,"open-source"]}]]],"relatedTags":[0],"authors":[1,[[0,{"name":[0,"John Graham-Cumming"],"slug":[0,"john-graham-cumming"],"bio":[0,null],"profile_image":[0,"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5vGNsXzZrtSLn2X30pnpUY/6f350e7dd36058a6422f9199b452bb02/john-graham-cumming.jpg"],"location":[0,"Lisbon, Portugal"],"website":[0,null],"twitter":[0,null],"facebook":[0,null]}]]],"meta_description":[0,null],"primary_author":[0,{}],"localeList":[0,{"name":[0,"Helping To Build Cloudflare, Part 4: Public Engagement Config"],"enUS":[0,"English for Locale"],"zhCN":[0,"No Page for Locale"],"zhHansCN":[0,"No Page for Locale"],"zhTW":[0,"No Page for Locale"],"frFR":[0,"No Page for Locale"],"deDE":[0,"No Page for Locale"],"itIT":[0,"No Page for Locale"],"jaJP":[0,"No Page for Locale"],"koKR":[0,"No Page for Locale"],"ptBR":[0,"No Page for Locale"],"esLA":[0,"No Page for Locale"],"esES":[0,"No Page for Locale"],"enAU":[0,"No Page for Locale"],"enCA":[0,"No Page for Locale"],"enIN":[0,"No Page for Locale"],"enGB":[0,"No Page for Locale"],"idID":[0,"No Page for Locale"],"ruRU":[0,"No Page for Locale"],"svSE":[0,"No Page for Locale"],"viVN":[0,"No Page for Locale"],"plPL":[0,"No Page for Locale"],"arAR":[0,"No Page for Locale"],"nlNL":[0,"No Page for Locale"],"thTH":[0,"No Page for Locale"],"trTR":[0,"No Page for Locale"],"heIL":[0,"No Page for Locale"],"lvLV":[0,"No Page for Locale"],"etEE":[0,"No Page for Locale"],"ltLT":[0,"No Page for Locale"]}],"url":[0,"https://blog.cloudflare.com/helping-to-build-cloudflare-part-4"],"metadata":[0,{"title":[0],"description":[0],"imgPreview":[0,""]}]}]]],"translations":[0,{"posts.by":[0,"By"],"footer.gdpr":[0,"GDPR"],"lang_blurb1":[0,"This post is also available in {lang1}."],"lang_blurb2":[0,"This post is also available in {lang1} and {lang2}."],"lang_blurb3":[0,"This post is also available in {lang1}, {lang2} and {lang3}."],"footer.press":[0,"Press"],"header.title":[0,"The Cloudflare Blog"],"search.clear":[0,"Clear"],"search.filter":[0,"Filter"],"search.source":[0,"Source"],"footer.careers":[0,"Careers"],"footer.company":[0,"Company"],"footer.support":[0,"Support"],"footer.the_net":[0,"theNet"],"search.filters":[0,"Filters"],"footer.our_team":[0,"Our team"],"footer.webinars":[0,"Webinars"],"page.more_posts":[0,"More posts"],"posts.time_read":[0,"{time} min read"],"search.language":[0,"Language"],"footer.community":[0,"Community"],"footer.resources":[0,"Resources"],"footer.solutions":[0,"Solutions"],"footer.trademark":[0,"Trademark"],"header.subscribe":[0,"Subscribe"],"footer.compliance":[0,"Compliance"],"footer.free_plans":[0,"Free plans"],"footer.impact_ESG":[0,"Impact/ESG"],"posts.follow_on_X":[0,"Follow on X"],"footer.help_center":[0,"Help center"],"footer.network_map":[0,"Network Map"],"header.please_wait":[0,"Please Wait"],"page.related_posts":[0,"Related posts"],"search.result_stat":[0,"Results <strong>{search_range}</strong> of <strong>{search_total}</strong> for <strong>{search_keyword}</strong>"],"footer.case_studies":[0,"Case Studies"],"footer.connect_2024":[0,"Connect 2024"],"footer.terms_of_use":[0,"Terms of Use"],"footer.white_papers":[0,"White Papers"],"footer.cloudflare_tv":[0,"Cloudflare TV"],"footer.community_hub":[0,"Community Hub"],"footer.compare_plans":[0,"Compare plans"],"footer.contact_sales":[0,"Contact Sales"],"header.contact_sales":[0,"Contact Sales"],"header.email_address":[0,"Email Address"],"page.error.not_found":[0,"Page not found"],"footer.developer_docs":[0,"Developer docs"],"footer.privacy_policy":[0,"Privacy Policy"],"footer.request_a_demo":[0,"Request a demo"],"page.continue_reading":[0,"Continue reading"],"footer.analysts_report":[0,"Analyst reports"],"footer.for_enterprises":[0,"For enterprises"],"footer.getting_started":[0,"Getting Started"],"footer.learning_center":[0,"Learning Center"],"footer.project_galileo":[0,"Project Galileo"],"pagination.newer_posts":[0,"Newer Posts"],"pagination.older_posts":[0,"Older Posts"],"posts.social_buttons.x":[0,"Discuss on X"],"search.source_location":[0,"Source/Location"],"footer.about_cloudflare":[0,"About Cloudflare"],"footer.athenian_project":[0,"Athenian Project"],"footer.become_a_partner":[0,"Become a partner"],"footer.cloudflare_radar":[0,"Cloudflare Radar"],"footer.network_services":[0,"Network services"],"footer.trust_and_safety":[0,"Trust & Safety"],"header.get_started_free":[0,"Get Started Free"],"page.search.placeholder":[0,"Search Cloudflare"],"footer.cloudflare_status":[0,"Cloudflare Status"],"footer.cookie_preference":[0,"Cookie Preferences"],"header.valid_email_error":[0,"Must be valid email."],"footer.connectivity_cloud":[0,"Connectivity cloud"],"footer.developer_services":[0,"Developer services"],"footer.investor_relations":[0,"Investor relations"],"page.not_found.error_code":[0,"Error Code: 404"],"footer.logos_and_press_kit":[0,"Logos & press kit"],"footer.application_services":[0,"Application services"],"footer.get_a_recommendation":[0,"Get a recommendation"],"posts.social_buttons.reddit":[0,"Discuss on Reddit"],"footer.sse_and_sase_services":[0,"SSE and SASE services"],"page.not_found.outdated_link":[0,"You may have used an outdated link, or you may have typed the address incorrectly."],"footer.report_security_issues":[0,"Report Security Issues"],"page.error.error_message_page":[0,"Sorry, we can't find the page you are looking for."],"header.subscribe_notifications":[0,"Subscribe to receive notifications of new posts:"],"footer.cloudflare_for_campaigns":[0,"Cloudflare for Campaigns"],"header.subscription_confimation":[0,"Subscription confirmed. Thank you for subscribing!"],"posts.social_buttons.hackernews":[0,"Discuss on Hacker News"],"footer.diversity_equity_inclusion":[0,"Diversity, equity & inclusion"],"footer.critical_infrastructure_defense_project":[0,"Critical Infrastructure Defense Project"]}]}" ssr="" client="load" opts="{"name":"MorePosts","value":true}" await-children=""> <div class="w-100 bt-l b--gray8"> <h3 data-testid="more-posts-title" class="orange fw5 f4 ph3 mt4">MORE POSTS</h3> </div> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2024-06-27T18:00:09.000+01:00">June 27, 2024 5:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/embedded-function-calling?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Embedded function calling in Workers AI: easier, smarter, faster</h6></a> <p class="gray1 lh-copy">Introducing a new way to do function calling in Workers AI by running function code alongside your inference. Plus, a new @cloudflare/ai-utils package to make getting started as simple as possible<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/harley?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Harley Turan</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/dhravya?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Dhravya Shah</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/michelle?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Michelle Chen</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/product-news?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Product News</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/workers-ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Workers AI</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developer-platform?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developer Platform</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2024-05-22T14:00:17.000+01:00">May 22, 2024 1:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/ai-gateway-is-generally-available?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">AI Gateway is generally available: a unified interface for managing and scaling your generative AI workloads</h6></a> <p class="gray1 lh-copy">AI Gateway is an AI ops platform that provides speed, reliability, and observability for your AI applications. With a single line of code, you can unlock powerful features including rate limiting, custom caching, real-time logs, and aggregated analytics across multiple providers<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/kathy?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Kathy Liao</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/michelle?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Michelle Chen</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/phil?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Phil Wittig</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/developer-platform?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developer Platform</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/workers-ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Workers AI</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/connectivity-cloud?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Connectivity Cloud</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2024-02-28T15:00:11.000+00:00">February 28, 2024 3:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/pingora-open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Open sourcing Pingora: our Rust framework for building programmable network services</h6></a> <p class="gray1 lh-copy">Pingora, our framework for building programmable and memory-safe network services, is now open source. Get started using Pingora today<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/yuchen?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Yuchen Wu</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/edward-h-wang?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Edward Wang</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/andrew-hauck?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Andrew Hauck</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/developer-platform?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developer Platform</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/rust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Rust</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/performance?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Performance</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2024-02-06T20:00:10.000+00:00">February 06, 2024 8:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/february-2024-workersai-catalog-update?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Adding new LLMs, text classification and code generation models to the Workers AI catalog</h6></a> <p class="gray1 lh-copy">Workers AI is now bigger and better with 8 new models and improved model performance<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/michelle?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Michelle Chen</a> </div></li> <li class="list flex items-center"> <div class="author-name-tooltip"> <span class="fw5 f2 black no-underline">, </span><a href="https://blog-cloudflare-com.translate.goog/author/logan?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Logan Grasby</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/workers-ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Workers AI</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/workers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare Workers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/ai?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">AI</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2024-01-24T14:00:17.000+00:00">January 24, 2024 2:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/introducing-foundations-our-open-source-rust-service-foundation-library?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Introducing Foundations - our open source Rust service foundation library</h6></a> <p class="gray1 lh-copy">Foundations is a foundational Rust library, designed to help scale programs for distributed, production-grade systems<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/ivan-nikulin?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Ivan Nikulin</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/rust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Rust</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/observability?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Observability</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/security?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Security</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/oxy?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Oxy</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2023-10-26T14:20:05.000+01:00">October 26, 2023 1:20 PM</p><a href="https://blog-cloudflare-com.translate.goog/introducing-har-sanitizer-secure-har-sharing?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Introducing HAR Sanitizer: secure HAR sharing</h6></a> <p class="gray1 lh-copy">As a follow-up to the most recent Okta breach, we are making a HAR file sanitizer available to everyone, not just Cloudflare customers, at no cost.<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/kenny?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Kenny Johnson</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/tools?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Tools</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2023-05-12T14:00:56.000+01:00">May 12, 2023 1:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/how-pingora-keeps-count?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">How Pingora keeps count</h6></a> <p class="gray1 lh-copy">In this blog post, we explain and open source the counting algorithm that powers Pingora. This will be the first of a series of blog posts that share both the Pingora libraries and the ideas behind them<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/yuchen?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Yuchen Wu</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/performance?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Performance</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/rust?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Rust</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2023-03-03T14:00:00.000+00:00">March 03, 2023 2:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/how-cloudflare-runs-prometheus-at-scale?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">How Cloudflare runs Prometheus at scale</h6></a> <p class="gray1 lh-copy">Here at Cloudflare we run over 900 instances of Prometheus with a total of around 4.9 billion time series. Operating such a large Prometheus deployment doesn’t come without challenges . In this blog post we’ll cover some of the issues we hit and how we solved them<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/lukasz?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Lukasz Mierzwa</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/prometheus?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Prometheus</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/observability?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Observability</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/deep-dive?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Deep Dive</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2022-09-27T14:01:00.000+01:00">September 27, 2022 1:01 PM</p><a href="https://blog-cloudflare-com.translate.goog/workerd-open-source-workers-runtime?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Introducing workerd: the Open Source Workers runtime</h6></a> <p class="gray1 lh-copy">workerd is the JavaScript/Wasm runtime code that powers Cloudflare Workers, now open source under the Apache 2.0 license<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/kenton-varda?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Kenton Varda</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/birthday-week?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Birthday Week</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/workers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare Workers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/serverless?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Serverless</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/product-news?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Product News</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2022-08-26T15:30:37.000+01:00">August 26, 2022 2:30 PM</p><a href="https://blog-cloudflare-com.translate.goog/open-sourcing-our-fork-of-pgbouncer?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Open sourcing our fork of PgBouncer</h6></a> <p class="gray1 lh-copy">We are releasing our internal fork of PgBouncer, filled with authentication bug fixes and new features around per user and connection pool isolation<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/justin-kwan?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Justin Kwan</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/edge-database?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Edge Database</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/internship-experience?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Internship Experience</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2022-08-03T14:00:00.000+01:00">August 03, 2022 1:00 PM</p><a href="https://blog-cloudflare-com.translate.goog/building-using-managed-components-webcm?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Building and using Managed Components with WebCM</h6></a> <p class="gray1 lh-copy">This is how Managed Components can be useful for you right now, if you manage a website or if you’re building third-party tools<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/yoav?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Yo'av Moshe</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/zaraz?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Zaraz</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/managed-components?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Managed Components</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2021-11-19T13:59:24.000+00:00">November 19, 2021 1:59 PM</p><a href="https://blog-cloudflare-com.translate.goog/production-saas-intro?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">An Open-Source CMS on the Cloudflare Stack: Introductory Post</h6></a> <p class="gray1 lh-copy">We are developing an example feature-complete SaaS application that will be built entirely on the Cloudflare stack.<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/lukeed?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Luke Edwards</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/full-stack-week?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Full Stack Week</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/workers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare Workers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developer-platform?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developer Platform</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2020-09-10T12:00:00.000+01:00">September 10, 2020 11:00 AM</p><a href="https://blog-cloudflare-com.translate.goog/migrating-cdnjs-to-serverless-with-workers-kv?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Migrating cdnjs to serverless with Workers KV</h6></a> <p class="gray1 lh-copy">Cloudflare powers cdnjs, an open-source project that delivers popular JavaScript libraries to over 11% of websites. Today, we are excited to announce its migration to a serverless infrastructure using Cloudflare Workers and its distributed key-value store Workers KV!<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/tyler?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Tyler Caslin</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/cdnjs?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">CDNJS</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/cloudflare-workers-kv?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare Workers KV</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/speed-and-reliability?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Speed & Reliability</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/serverless?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Serverless</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2019-12-19T19:30:19.000+00:00">December 19, 2019 7:30 PM</p><a href="https://blog-cloudflare-com.translate.goog/an-update-on-cdnjs?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">An Update on CDNJS</h6></a> <p class="gray1 lh-copy">CDNJS powers over 10% of the web. A month ago it needed our help, now it needs yours.<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/zack-bloom?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">Zack Bloom</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/cdnjs?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">CDNJS</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/javascript?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">JavaScript</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/developers?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Developers</a> </div> </div> </article> <article data-testid="more-posts-article" class="w-100 w-100-m ph3 mb4"> <p class="f3 fw5 gray1" data-iso-date="2019-02-04T08:41:00.000+00:00">February 04, 2019 8:41 AM</p><a href="https://blog-cloudflare-com.translate.goog/helping-to-build-cloudflare-part-4?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline gray1 f4 fw5"><h6 class="gray1 f4 fw5 mt2">Helping To Build Cloudflare, Part 4: Public Engagement</h6></a> <p class="gray1 lh-copy">We don’t believe that any of our software, not a single line of code, provides us with a long-term advantage. We could, today, open source every single line of code at Cloudflare and we don’t believe we’d be hurt by it.<!-- -->...</p> <ul class="flex pl0 fw6 f2"> <span>By<!-- --> </span> <li class="list flex items-center"> <div class="author-name-tooltip"> <a href="https://blog-cloudflare-com.translate.goog/author/john-graham-cumming?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="fw5 f2 black no-underline">John Graham-Cumming</a> </div></li> </ul> <div class="flex flex-row flex-wrap"> <div> <a href="https://blog-cloudflare-com.translate.goog/tag/life-at-cloudflare?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Life at Cloudflare</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/cloudflare-history?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare History</a> </div> <div> <span class="f1 fw2 blue3 no-underline underline-hover">, </span><a href="https://blog-cloudflare-com.translate.goog/tag/open-source?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" class="no-underline f1 fw2 blue3 underline-hover">Open Source</a> </div> </div> </article><!--astro:end--> </astro-island> <div class="pagination mw-100 center mv5 ph3 w-100 tc"> <div class="center w-50-l w-100"> <div class="flex items-center justify-center justify-around-m "> <ul class="flex list ml3" style="padding-inline-start:inherit"> <li class="gray"><a class="no-underline underline-hover dib-m dib-l mr1 gray3 " href="https://blog-cloudflare-com.translate.goog/tag/open-source/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB">1</a></li> <li class=""><a class="no-underline underline-hover dib-m dib-l mr1 blue3" href="https://blog-cloudflare-com.translate.goog/tag/open-source/page/2?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB">2</a></li> <li class="ml">…</li> <li><a class="no-underline underline-hover dib-m dib-l mr3 blue3" href="https://blog-cloudflare-com.translate.goog/tag/open-source/page/2?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB">2</a></li> </ul><span><a class="no-underline blue3 underline-hover" data-testid="pagination-toggle-next" href="https://blog-cloudflare-com.translate.goog/tag/open-source/page/2?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB" rel="prev"><span class="underline-hover dn dib-m dib-l">Older Posts</span> →</a></span> </div> </div> </div> </main> <footer class="pt4 pb4 pl1 pr1 main-footer"> <div class="mw8 center dn db-l ph3"> <div class="flex flex-row justify-between"> <div class="main-footer__menu-group"> <ul id="getting-started-menu" class="list pl0"> <li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="getting-started-menu">Getting Started<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/plans/free/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="free-plans" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Free plans</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/enterprise/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="enterprise" class="f1 blue3 no-underline underline-hover" rel="noreferrer">For enterprises</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/plans/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="compare-plans" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Compare plans</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/about-your-website/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="get-a-recommendation" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Get a recommendation</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/plans/enterprise/demo/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="request-a-demo" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Request a demo</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/plans/enterprise/contact/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="contact-sales" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Contact Sales</a></li> </ul> </div> <div class="main-footer__menu-group"> <ul id="company-menu" class="list pl0"> <li class="pt1 pb1 f1" data-submenu="company-menu">Resources<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/learning/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="learning-center" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Learning Center</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/analysts/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="analysts-report" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Analyst reports</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://radar.cloudflare.com/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="overview" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare Radar</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://cloudflare.tv/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="tv" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare TV</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/case-studies/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="case-studies" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Case Studies</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/resource-hub/?resourcetype%3DWebinar" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="webinars" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Webinars</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/resource-hub/?resourcetype%3DWhitepaper" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="white-papers" class="f1 blue3 no-underline underline-hover" rel="noreferrer">White Papers</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://developers.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="developer-docs" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Developer docs</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/the-net/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="theNet" class="f1 blue3 no-underline underline-hover" rel="noreferrer">theNet</a></li> </ul> </div> <div class="main-footer__menu-group"> <ul id="sales-menu" class="list pl0"> <li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="sales-menu">Solutions<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/connectivity-cloud/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="connectivity-cloud" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Connectivity cloud</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/zero-trust/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="zero-trust" class="f1 blue3 no-underline underline-hover" rel="noreferrer">SSE and SASE services</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/application-services/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="application-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Application services</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/network-services/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="network-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Network services</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/developer-platform/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="developer-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Developer services</a></li> </ul> </div> <div class="main-footer__menu-group"> <ul id="community-menu" class="list pl0"> <li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="community-menu">Community<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://community.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="community_hub" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Community Hub</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/galileo/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="galileo" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Project Galileo</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/athenian/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="athenian" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Athenian Project</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/campaigns/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="cloudflare-for-campaigns" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare for Campaigns</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/partners/technology-partners/cidp/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="critical-infrastructure-defense-project" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Critical Infrastructure Defense Project</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/connect2024/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="connect-2024" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Connect 2024</a></li> </ul> </div> <div class="main-footer__menu-group"> <ul id="support-menu" class="list pl0"> <li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="support-menu">Support<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://support.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="help-center" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Help center</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflarestatus.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="status" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare Status</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/compliance/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="compliance" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Compliance</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/gdpr/introduction/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="gdpr" class="f1 blue3 no-underline underline-hover" rel="noreferrer">GDPR</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/trust-hub/abuse-approach/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="trust-and-safety" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Trust & Safety</a></li> </ul> </div> <div class="main-footer__menu-group"> <ul id="company-menu" class="list pl0"> <li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="company-menu">Company<i class="icon-caret-down"></i></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/about-overview/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="overview" class="f1 blue3 no-underline underline-hover" rel="noreferrer">About Cloudflare</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/people/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="our_team" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Our team</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://cloudflare.net/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="investor-relations" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Investor relations</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/press/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="press" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Press</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/careers/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="careers" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Careers</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/diversity-equity-and-inclusion/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="diversity-equity-inclusion" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Diversity, equity & inclusion</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/impact/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="impact-ESG" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Impact/ESG</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/network/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="network_map" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Network Map</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/press-kit/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="press-kit" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Logos & press kit</a></li> <li class="pt1 pb1"><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/partners/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="partners" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Become a partner</a></li> </ul> </div> </div> </div> <div class="mw8 center ph3"> <div class="flex flex-row flex-wrap justify-center md:justify-between items-center pt4"> <div class="flex flex-row space-x-4 items-start w-25-l pb4 pb0-l"> <a target="_blank" rel="noreferrer" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.facebook.com/Cloudflare/" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/facebook.svg" alt="facebook"></a><a target=" _blank" rel="noreferrer" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://x.com/Cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/twitter.svg" alt="X"></a><a target="_blank" rel="noreferrer" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.linkedin.com/company/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/linkedin.svg" alt="linkedin"></a><a target="_blank" rel="noreferrer" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.youtube.com/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/youtube.svg" alt="youtube"></a><a target="_blank" rel="noreferrer" href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.instagram.com/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/instagram.svg" alt="instagram"></a> </div> <div class="w-70-l tr-l tl-ns"> <div> <span class="main-footer__copyright f1">© <!-- -->2024<!-- --> Cloudflare, Inc.<!-- --> </span><span class="main-footer__copyright f1">|</span><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/privacypolicy/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Privacy Policy<!-- --> </a><span class="main-footer__copyright f1">|</span><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/website-terms/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Terms of Use<!-- --> </a><span class="main-footer__copyright f1">|</span><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/disclosure/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Report Security Issues<!-- --> </a><span class="main-footer__copyright f1">|</span><img class="mw2 ph1" src="/images/privacy-options.svg" alt="Privacy Options"><a href="https://blog-cloudflare-com.translate.goog/tag/open-source/?_x_tr_sl=pl&_x_tr_tl=de&_x_tr_hl=en-GB#cookie-settings" id="ot-sdk-btn" class="ot-sdk-show-settings main-footer__copyright f1 no-underline underline-hover"><span class="brandGray5">Cookie Preferences</span> </a><span class="main-footer__copyright f1">|</span><a href="https://translate.google.com/website?sl=pl&tl=de&hl=en-GB&u=https://www.cloudflare.com/trademark/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Trademark<!-- --> </a> </div> </div> </div> </div> </footer> <script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{"rayId":"8e7b3eb79eb63fce","version":"2024.10.5","serverTiming":{"name":{"cfExtPri":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}},"token":"2bc156e5f250476cb274d269511ffb57","b":1}" crossorigin="anonymous"></script> <script>function gtElInit() {var lib = new google.translate.TranslateService();lib.translatePage('pl', 'de', function () {});}</script> <script src="https://translate.google.com/translate_a/element.js?cb=gtElInit&hl=en-GB&client=wt" type="text/javascript"></script> </body> </html>