[{"data":1,"prerenderedAt":1576},["ShallowReactive",2],{"navigation-en":3,"content-urls":13,"pagesContext-en-2":224,"pagesContext-en-3":251,"authorsContext-en-jeroenbach":271,"postsContext-en-2-undefined":288},[4,7,10],{"label":5,"to":6},"Blog","\u002Fposts",{"label":8,"to":9},"Portfolio","\u002Fcontent\u002F20-portfolio",{"label":11,"to":12},"About","\u002Fcontent\u002F30-about",{"page":14,"blogPost":101,"blog":223},{"1":15,"10":37,"20":51,"30":65,"404":82},{"en":16,"fr":25,"nl":28,"de":31,"es":34},{"title":17,"path":18,"stem":19,"contentId":20,"url":21,"dateModified":22,"locale":23,"type":24},"Home","\u002Fcontent\u002Fhome","content\u002F1.home",1,"","2025-11-02T18:00:00","en","page",{"title":17,"path":18,"stem":19,"contentId":20,"url":26,"dateModified":22,"locale":27,"type":24},"\u002Ffr","fr",{"title":17,"path":18,"stem":19,"contentId":20,"url":29,"dateModified":22,"locale":30,"type":24},"\u002Fnl","nl",{"title":17,"path":18,"stem":19,"contentId":20,"url":32,"dateModified":22,"locale":33,"type":24},"\u002Fde","de",{"title":17,"path":18,"stem":19,"contentId":20,"url":35,"dateModified":22,"locale":36,"type":24},"\u002Fes","es",{"en":38,"fr":43,"nl":45,"de":47,"es":49},{"title":5,"path":39,"stem":40,"contentId":41,"url":6,"dateModified":42,"locale":23,"type":24},"\u002Fcontent\u002Fposts","content\u002F10.posts",10,"2025-10-26T18:00:00",{"title":5,"path":39,"stem":40,"contentId":41,"url":44,"dateModified":42,"locale":27,"type":24},"\u002Ffr\u002Farticles",{"title":5,"path":39,"stem":40,"contentId":41,"url":46,"dateModified":42,"locale":30,"type":24},"\u002Fnl\u002Fposts",{"title":5,"path":39,"stem":40,"contentId":41,"url":48,"dateModified":42,"locale":33,"type":24},"\u002Fde\u002Fposts",{"title":5,"path":39,"stem":40,"contentId":41,"url":50,"dateModified":42,"locale":36,"type":24},"\u002Fes\u002Farticulos",{"en":52,"fr":56,"nl":58,"de":60,"es":62},{"title":8,"path":53,"stem":54,"contentId":55,"url":9,"dateModified":42,"locale":23,"type":24},"\u002Fcontent\u002Fportfolio","content\u002F20.portfolio",20,{"title":8,"path":53,"stem":54,"contentId":55,"url":57,"dateModified":42,"locale":27,"type":24},"\u002Ffr\u002Fcontent\u002F20-portfolio",{"title":8,"path":53,"stem":54,"contentId":55,"url":59,"dateModified":42,"locale":30,"type":24},"\u002Fnl\u002Fcontent\u002F20-portfolio",{"title":8,"path":53,"stem":54,"contentId":55,"url":61,"dateModified":42,"locale":33,"type":24},"\u002Fde\u002Fcontent\u002F20-portfolio",{"title":63,"path":53,"stem":54,"contentId":55,"url":64,"dateModified":42,"locale":36,"type":24},"Portafolio","\u002Fes\u002Fcontent\u002F20-portafolio",{"en":66,"fr":70,"nl":73,"de":76,"es":79},{"title":11,"path":67,"stem":68,"contentId":69,"url":12,"dateModified":42,"locale":23,"type":24},"\u002Fcontent\u002Fabout","content\u002F30.about",30,{"title":71,"path":67,"stem":68,"contentId":69,"url":72,"dateModified":42,"locale":27,"type":24},"À propos","\u002Ffr\u002Fcontent\u002F30-a-propos",{"title":74,"path":67,"stem":68,"contentId":69,"url":75,"dateModified":42,"locale":30,"type":24},"Over","\u002Fnl\u002Fcontent\u002F30-over",{"title":77,"path":67,"stem":68,"contentId":69,"url":78,"dateModified":42,"locale":33,"type":24},"Über mich","\u002Fde\u002Fcontent\u002F30-uber-mich",{"title":80,"path":67,"stem":68,"contentId":69,"url":81,"dateModified":42,"locale":36,"type":24},"Acerca de","\u002Fes\u002Fcontent\u002F30-acerca-de",{"en":83,"fr":89,"nl":92,"de":95,"es":98},{"title":84,"path":85,"stem":86,"contentId":87,"url":88,"dateModified":22,"locale":23,"type":24},"Page not found","\u002Fcontent\u002Fpage-not-found","content\u002F404.page-not-found",404,"\u002Fcontent\u002F404-page-not-found",{"title":90,"path":85,"stem":86,"contentId":87,"url":91,"dateModified":22,"locale":27,"type":24},"Page non trouvée","\u002Ffr\u002Fcontent\u002F404-page-non-trouvee",{"title":93,"path":85,"stem":86,"contentId":87,"url":94,"dateModified":22,"locale":30,"type":24},"Pagina niet gevonden","\u002Fnl\u002Fcontent\u002F404-pagina-niet-gevonden",{"title":96,"path":85,"stem":86,"contentId":87,"url":97,"dateModified":22,"locale":33,"type":24},"Seite nicht gefunden","\u002Fde\u002Fcontent\u002F404-seite-nicht-gefunden",{"title":99,"path":85,"stem":86,"contentId":87,"url":100,"dateModified":22,"locale":36,"type":24},"Página no encontrada","\u002Fes\u002Fcontent\u002F404-pagina-no-encontrada",{"1":102,"2":126,"3":150,"4":175,"5":199},{"en":103,"fr":110,"nl":115,"de":118,"es":121},{"title":104,"path":105,"stem":106,"contentId":20,"dateModified":107,"url":108,"locale":23,"type":109},"Mastering Conditional Property Types with Vue 3.3 Generics","\u002Fposts\u002F1","posts\u002F1","2025-05-27T11:30:00","\u002Fposts\u002F1-mastering-conditional-property-types-with-vue-3_3-generics","blogPost",{"title":111,"path":112,"stem":113,"contentId":20,"dateModified":107,"url":114,"locale":27,"type":109},"Maîtriser les types de propriétés conditionnelles avec les génériques de Vue 3.3","\u002Farticles\u002F1","articles\u002F1","\u002Ffr\u002Farticles\u002F1-maitriser-les-types-de-proprietes-conditionnelles-avec-les-generiques-de-vue-3_3",{"title":116,"path":105,"stem":106,"contentId":20,"dateModified":107,"url":117,"locale":30,"type":109},"Conditionele Property Types beheersen met Vue 3.3 Generics","\u002Fnl\u002Fposts\u002F1-conditionele-property-types-beheersen-met-vue-3_3-generics",{"title":119,"path":105,"stem":106,"contentId":20,"dateModified":107,"url":120,"locale":33,"type":109},"Bedingte Property-Typen mit Vue 3.3 Generics meistern","\u002Fde\u002Fposts\u002F1-bedingte-property-typen-mit-vue-3_3-generics-meistern",{"title":122,"path":123,"stem":124,"contentId":20,"dateModified":107,"url":125,"locale":36,"type":109},"Dominar los tipos de propiedades condicionales con genéricos de Vue 3.3","\u002Farticulos\u002F1","articulos\u002F1","\u002Fes\u002Farticulos\u002F1-dominar-los-tipos-de-propiedades-condicionales-con-genericos-de-vue-3_3",{"en":127,"fr":134,"nl":139,"de":142,"es":145},{"title":128,"path":129,"stem":130,"contentId":131,"dateModified":132,"url":133,"locale":23,"type":109},"Ditching the Cookie Banners: Run Plausible Analytics on Azure Kubernetes","\u002Fposts\u002F2","posts\u002F2",2,"2025-10-04T13:00:00","\u002Fposts\u002F2-ditching-the-cookie-banners:-run-plausible-analytics-on-azure-kubernetes",{"title":135,"path":136,"stem":137,"contentId":131,"dateModified":132,"url":138,"locale":27,"type":109},"Éliminer les bannières de cookies: Exécutez Plausible Analytics sur Azure Kubernetes","\u002Farticles\u002F2","articles\u002F2","\u002Ffr\u002Farticles\u002F2-eliminer-les-bannieres-de-cookies:-executez-plausible-analytics-sur-azure-kubernetes",{"title":140,"path":129,"stem":130,"contentId":131,"dateModified":132,"url":141,"locale":30,"type":109},"Afscheid van Cookie Banners: Draai Plausible Analytics op Azure Kubernetes","\u002Fnl\u002Fposts\u002F2-afscheid-van-cookie-banners:-draai-plausible-analytics-op-azure-kubernetes",{"title":143,"path":129,"stem":130,"contentId":131,"dateModified":132,"url":144,"locale":33,"type":109},"Abschied von Cookie-Bannern: Plausible Analytics auf Azure Kubernetes betreiben","\u002Fde\u002Fposts\u002F2-abschied-von-cookie-bannern:-plausible-analytics-auf-azure-kubernetes-betreiben",{"title":146,"path":147,"stem":148,"contentId":131,"dateModified":132,"url":149,"locale":36,"type":109},"Eliminar los banners de cookies: Ejecuta Plausible Analytics en Azure Kubernetes","\u002Farticulos\u002F2","articulos\u002F2","\u002Fes\u002Farticulos\u002F2-eliminar-los-banners-de-cookies:-ejecuta-plausible-analytics-en-azure-kubernetes",{"en":151,"fr":158,"nl":164,"de":167,"es":170},{"title":152,"path":153,"stem":154,"contentId":155,"dateModified":156,"url":157,"locale":23,"type":109},"Track how many people read your articles, using Plausible.io, Vue.js and Azure functions","\u002Fposts\u002F3","posts\u002F3",3,"2025-08-03T15:45:00","\u002Fposts\u002F3-track-how-many-people-read-your-articles-using-plausible_io-vue_js-and-azure-functions",{"title":159,"path":160,"stem":161,"contentId":155,"dateModified":162,"url":163,"locale":27,"type":109},"Suivez combien de personnes lisent vos articles, en utilisant Plausible.io, Vue.js et Azure Functions","\u002Farticles\u002F3","articles\u002F3","2025-08-15:45:00","\u002Ffr\u002Farticles\u002F3-suivez-combien-de-personnes-lisent-vos-articles-en-utilisant-plausible_io-vue_js-et-azure-functions",{"title":165,"path":153,"stem":154,"contentId":155,"dateModified":156,"url":166,"locale":30,"type":109},"Volg hoeveel mensen je artikelen lezen, met Plausible.io, Vue.js en Azure functions","\u002Fnl\u002Fposts\u002F3-volg-hoeveel-mensen-je-artikelen-lezen-met-plausible_io-vue_js-en-azure-functions",{"title":168,"path":153,"stem":154,"contentId":155,"dateModified":156,"url":169,"locale":33,"type":109},"Verfolgen Sie, wie viele Menschen Ihre Artikel lesen, mit Plausible.io, Vue.js und Azure Functions","\u002Fde\u002Fposts\u002F3-verfolgen-sie-wie-viele-menschen-ihre-artikel-lesen-mit-plausible_io-vue_js-und-azure-functions",{"title":171,"path":172,"stem":173,"contentId":155,"dateModified":156,"url":174,"locale":36,"type":109},"Rastrea cuántas personas leen tus artículos, usando Plausible.io, Vue.js y funciones de Azure","\u002Farticulos\u002F3","articulos\u002F3","\u002Fes\u002Farticulos\u002F3-rastrea-cuantas-personas-leen-tus-articulos-usando-plausible_io-vue_js-y-funciones-de-azure",{"en":176,"fr":183,"nl":188,"de":191,"es":194},{"title":177,"path":178,"stem":179,"contentId":180,"dateModified":181,"url":182,"locale":23,"type":109},"Deploy a production-ready Kubernetes Cluster on Azure with Terraform","\u002Fposts\u002F4","posts\u002F4",4,"2025-12-06T21:00:00","\u002Fposts\u002F4-deploy-a-production-ready-kubernetes-cluster-on-azure-with-terraform",{"title":184,"path":185,"stem":186,"contentId":180,"dateModified":181,"url":187,"locale":27,"type":109},"Déployer un cluster Kubernetes prêt pour la production sur Azure avec Terraform","\u002Farticles\u002F4","articles\u002F4","\u002Ffr\u002Farticles\u002F4-deployer-un-cluster-kubernetes-pret-pour-la-production-sur-azure-avec-terraform",{"title":189,"path":178,"stem":179,"contentId":180,"dateModified":181,"url":190,"locale":30,"type":109},"Implementeer een productie-klaar Kubernetes Cluster op Azure met Terraform","\u002Fnl\u002Fposts\u002F4-implementeer-een-productie-klaar-kubernetes-cluster-op-azure-met-terraform",{"title":192,"path":178,"stem":179,"contentId":180,"dateModified":181,"url":193,"locale":33,"type":109},"Einen produktionsreifen Kubernetes-Cluster auf Azure mit Terraform bereitstellen","\u002Fde\u002Fposts\u002F4-einen-produktionsreifen-kubernetes-cluster-auf-azure-mit-terraform-bereitstellen",{"title":195,"path":196,"stem":197,"contentId":180,"dateModified":181,"url":198,"locale":36,"type":109},"Despliega un clúster de Kubernetes listo para producción en Azure con Terraform","\u002Farticulos\u002F4","articulos\u002F4","\u002Fes\u002Farticulos\u002F4-despliega-un-cluster-de-kubernetes-listo-para-produccion-en-azure-con-terraform",{"en":200,"fr":207,"nl":212,"de":215,"es":218},{"title":201,"path":202,"stem":203,"contentId":204,"dateModified":205,"url":206,"locale":23,"type":109},"Array to Map conversion in Typescript, with type safety","\u002Fposts\u002F5","posts\u002F5",5,"2025-09-29T21:00:00","\u002Fposts\u002F5-array-to-map-conversion-in-typescript-with-type-safety",{"title":208,"path":209,"stem":210,"contentId":204,"dateModified":205,"url":211,"locale":27,"type":109},"Conversion de tableau en Map en Typescript, avec sécurité des types","\u002Farticles\u002F5","articles\u002F5","\u002Ffr\u002Farticles\u002F5-conversion-de-tableau-en-map-en-typescript-avec-securite-des-types",{"title":213,"path":202,"stem":203,"contentId":204,"dateModified":205,"url":214,"locale":30,"type":109},"Array naar Map conversie in Typescript, met type veiligheid","\u002Fnl\u002Fposts\u002F5-array-naar-map-conversie-in-typescript-met-type-veiligheid",{"title":216,"path":202,"stem":203,"contentId":204,"dateModified":205,"url":217,"locale":33,"type":109},"Array zu Map Konvertierung in TypeScript, mit Typsicherheit","\u002Fde\u002Fposts\u002F5-array-zu-map-konvertierung-in-typescript-mit-typsicherheit",{"title":219,"path":220,"stem":221,"contentId":204,"dateModified":205,"url":222,"locale":36,"type":109},"Conversión de Array a Map en Typescript, con seguridad de tipos","\u002Farticulos\u002F5","articulos\u002F5","\u002Fes\u002Farticulos\u002F5-conversion-de-array-a-map-en-typescript-con-seguridad-de-tipos",{},{"id":225,"title":226,"body":227,"canonicalUrl":236,"company":236,"contentId":131,"dateModified":236,"datePublished":236,"description":233,"draft":237,"enableProse":237,"excludeFromNavigation":237,"extension":238,"imageAlt":236,"imageUrl":236,"meta":239,"navigation":245,"partial":245,"path":246,"seo":247,"slug":236,"stem":248,"url":249,"__hash__":250},"pages_en\u002Fcontent\u002F_footer.md","About Jeroen Bach",{"type":228,"value":229,"toc":234},"minimark",[230],[231,232,233],"p",{},"Designed in Figma and built with Vue.js, Nuxt.js and Tailwind CSS.\nDeployed via Azure Static Web App and Azure Functions.\nWebsite analytics are powered by Plausible Analytics, deployed using Azure Kubernetes Service.",{"title":21,"searchDepth":131,"depth":131,"links":235},[],null,false,"md",{"readingTime":240},{"text":241,"minutes":242,"time":243,"words":244},"1 min read",0.16,9600,32,true,"\u002Fcontent\u002F_footer",{"title":226,"description":233},"content\u002F_footer","\u002Fcontent\u002F2-about-jeroen-bach","tgPfOB73xkNtsI3Ers_EH08FHEHR7ko45cqNiVgrM0s",{"id":252,"title":253,"body":254,"canonicalUrl":236,"company":236,"contentId":155,"dateModified":236,"datePublished":236,"description":258,"draft":237,"enableProse":237,"excludeFromNavigation":237,"extension":238,"imageAlt":236,"imageUrl":236,"meta":261,"navigation":245,"partial":245,"path":266,"seo":267,"slug":236,"stem":268,"url":269,"__hash__":270},"pages_en\u002Fcontent\u002F_footer-about.md","Footer About",{"type":228,"value":255,"toc":259},[256],[231,257,258],{},"I'm a Software Engineer and Team Lead with over 15 years of professional experience.\nI'm passionate about solving complex problems through simple, elegant solutions.\nThis blog is where I share techniques and insights for building great software, inspired by real-world projects.",{"title":21,"searchDepth":131,"depth":131,"links":260},[],{"readingTime":262},{"text":241,"minutes":263,"time":264,"words":265},0.205,12300,41,"\u002Fcontent\u002F_footer-about",{"description":258},"content\u002F_footer-about","\u002Fcontent\u002F3-footer-about","49ofD3QAU2eKgnsoCR9zQhGEP7EECAsjUJeOHSF1GJI",{"id":272,"company":273,"extension":277,"fullName":278,"github":279,"homePage":280,"imageUrl":276,"linkedIn":281,"meta":282,"role":283,"stem":284,"twitter":285,"userName":286,"__hash__":287},"authors_en\u002Fauthors\u002Fjeroenbach.yaml",{"name":274,"url":275,"imageUrl":276},"Bach.Software","https:\u002F\u002Fbach.software","\u002FJEROEN-_A7R5652-HD-SQUARE-zoom.jpg","yaml","Jeroen Bach","https:\u002F\u002Fgithub.com\u002Fjeroenbach","https:\u002F\u002Fbach.software\u002Fpages\u002Fabout","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fjeroenbach\u002F",{},"Software Engineer \u002F Team Lead","authors\u002Fjeroenbach","https:\u002F\u002Fx.com\u002Fjeroenbach","jeroenbach","QjQ1vpL_EQbLZNaZdDNOjIkS-thm6oxHuAdoNIauNI8",{"id":289,"title":128,"authorName":286,"body":290,"canonicalUrl":236,"category":1553,"contentId":131,"dateModified":132,"datePublished":1554,"description":1555,"draft":237,"excerpt":1556,"extension":238,"imageAlt":1560,"imagePosition":1561,"imageUrl":1562,"keywords":1563,"meta":1565,"navigation":245,"path":129,"readingTime":1566,"seo":1571,"slug":236,"stem":130,"url":133,"__hash__":1572,"author":1573},"posts_en\u002Fposts\u002F2.md",{"type":228,"value":291,"toc":1547},[292,295,306,325,330,333,336,346,350,353,356,360,363,366,373,379,382,410,415,422,433,436,439,706,720,1104,1111,1126,1133,1156,1434,1443,1448,1452,1482,1485,1514,1521,1535,1538,1543],[231,293,294],{},"For quite some time, I've been looking for ways to gather website analytics without showing those annoying consent banners.\nIf you've ever wanted website analytics without compromising privacy or user experience, this article is for you.",[231,296,297,298,305],{},"In this guide, I'll introduce you to ",[299,300,304],"a",{"href":301,"rel":302},"https:\u002F\u002Fplausible.io\u002F",[303],"nofollow","Plausible Analytics"," - a lightweight, open-source, privacy-focused alternative to Google Analytics -\nand walk you through setting it up on an affordable Kubernetes cluster in Azure.",[307,308,309],"blockquote",{},[231,310,311,315,316,320,321,324],{},[312,313,314],"strong",{},"Update (Aug 2025):"," Azure has retired the ",[317,318,319],"em",{},"Basic Load Balancer"," SKU. If you are following this guide, make sure to use the ",[312,322,323],{},"Standard Load Balancer"," instead.\nThe Standard SKU adds around €18,- euro to your monthly cost, but is now the only supported option for AKS clusters.",[326,327,329],"h2",{"id":328},"what-is-plausibleio","What is Plausible.io?",[231,331,332],{},"Plausible.io is a simple yet powerful analytics tool designed with privacy in mind. Unlike Google Analytics,\nPlausible doesn't use cookies or collect personally identifiable information. As a result, you can skip the cookie consent banner altogether\nand still remain compliant with GDPR and other privacy laws.",[231,334,335],{},"It's a great option for websites that care about usability, performance, and respecting visitor privacy.",[231,337,338,339,345],{},"The best part? You can run it yourself. You can start with their ",[299,340,344],{"href":341,"rel":342,":target":343},"https:\u002F\u002Fplausible.io\u002F#pricing",[303],"\\_blank","hosted cloud version",", which comes with a free trial -\nperfect if you want to test it out without any setup hassle. If you like what you see, you can either stick with their subscription or take full control by self-hosting it.",[326,347,349],{"id":348},"self-hosting-vs-hosted-solution","Self-hosting vs. Hosted Solution",[231,351,352],{},"Self-hosting is ideal if you're a developer like me and happen to have some extra Azure credit lying around.\nIt gives you full control and can be quite cost-effective. But if that's not your situation, the hosted solution is probably the more economical and hassle-free choice.",[231,354,355],{},"Either way, Plausible makes it easy to get started - and in this article, I'll show you how to set it up yourself using Docker and Kubernetes on Azure.",[326,357,359],{"id":358},"affordable-plausibleio-hosting-with-azure-kubernetes","Affordable Plausible.io Hosting with Azure Kubernetes",[231,361,362],{},"In this article, I'll show you the cheapest way to host Plausible.io on Azure Kubernetes Service (AKS).",[231,364,365],{},"To give you an idea of what to expect: running this setup for a full month costs me around €35 and that includes daily incremental backups.",[307,367,368],{},[231,369,370,372],{},[312,371,314],{}," Due to the costs of the Standard Load Balancer this will now be around €53.",[374,375],"post-image",{"alt":376,"caption":376,"css-class":377,"src":378},"Azure Kubernetes costs for Plausible.io","aspect-[735\u002F560]","posts\u002F2\u002Fplausible-costs.jpg",[231,380,381],{},"This setup includes the following key components:",[383,384,385,392,398,404],"ul",{},[386,387,388,391],"li",{},[312,389,390],{},"cert-manager"," - to manage SSL\u002FTLS certificates automatically",[386,393,394,397],{},[312,395,396],{},"Let's Encrypt"," - for issuing free, trusted SSL certificates",[386,399,400,403],{},[312,401,402],{},"NGINX Ingress Controller"," - to handle routing and expose Plausible to the internet",[386,405,406,409],{},[312,407,408],{},"Plausible.io"," - the privacy-friendly analytics platform we're deploying",[411,412,414],"h4",{"id":413},"storage-tip-use-ephemeral-disks-for-cost-savings","Storage Tip: Use Ephemeral Disks for Cost Savings",[231,416,417,418,421],{},"When creating an AKS cluster, one of the main extra costs comes from the ",[312,419,420],{},"persistent OS disk"," that's created by default for each node.",[231,423,424,425,428,429,432],{},"To reduce costs, you can use ",[312,426,427],{},"ephemeral OS disks"," instead. These are stored directly on the VM's local storage and come at ",[312,430,431],{},"no additional cost",".\nThey're ideal for stateless workloads or setups where persistent storage is handled separately - like in this case, where\nwe store Plausible's data on an attached volume.",[231,434,435],{},"If you go this route, make sure to check the maximum disk size allowed for your selected VM size.\nFor example, the Standard_B2s VM size supports up to 30 GB of ephemeral storage - which is more than enough for storing the necessary Docker images.",[231,437,438],{},"In the step-by-step guide below, you can run the commands directly in Azure Cloud Shell.",[440,441,442],"code-group",{},[443,444,449],"pre",{"className":445,"code":446,"filename":447,"language":448,"meta":21,"style":21},"language-sh shiki shiki-themes github-light github-dark","#bash\nRESOURCEGROUP=rg-plausible-westeu-prod\nCLUSTERNAME=aks-plausible-westeu-prod\nLOCATION=westeurope\nVMSIZE=Standard_B2s\nVMDISKSIZE=30 # Keep this to the max size the VM allows (this is the max of Standard_B2s)\n\n# Create the resource group\naz group create --name $RESOURCEGROUP --location $LOCATION\n\n# Create the AKS cluster - with the Standard Loadbalancer\naz aks create --resource-group $RESOURCEGROUP --name $CLUSTERNAME \\\n  --node-vm-size $VMSIZE \\\n  --node-count 1 \\\n  --node-resource-group ${RESOURCEGROUP}-nodes \\\n  --load-balancer-sku standard \\\n  --generate-ssh-keys \\\n  --location $LOCATION \\\n  --node-osdisk-type Ephemeral \\\n  --node-osdisk-size $VMDISKSIZE\n\n# Get the credentials for the AKS cluster (for the following steps)\naz aks get-credentials --resource-group $RESOURCEGROUP --name $CLUSTERNAME\n","Create cluster","sh",[450,451,452,460,474,484,494,504,518,524,530,556,560,566,590,601,613,627,638,646,657,668,676,681,687],"code",{"__ignoreMap":21},[453,454,456],"span",{"class":455,"line":20},"line",[453,457,459],{"class":458},"sJ8bj","#bash\n",[453,461,462,466,470],{"class":455,"line":131},[453,463,465],{"class":464},"sVt8B","RESOURCEGROUP",[453,467,469],{"class":468},"szBVR","=",[453,471,473],{"class":472},"sZZnC","rg-plausible-westeu-prod\n",[453,475,476,479,481],{"class":455,"line":155},[453,477,478],{"class":464},"CLUSTERNAME",[453,480,469],{"class":468},[453,482,483],{"class":472},"aks-plausible-westeu-prod\n",[453,485,486,489,491],{"class":455,"line":180},[453,487,488],{"class":464},"LOCATION",[453,490,469],{"class":468},[453,492,493],{"class":472},"westeurope\n",[453,495,496,499,501],{"class":455,"line":204},[453,497,498],{"class":464},"VMSIZE",[453,500,469],{"class":468},[453,502,503],{"class":472},"Standard_B2s\n",[453,505,507,510,512,515],{"class":455,"line":506},6,[453,508,509],{"class":464},"VMDISKSIZE",[453,511,469],{"class":468},[453,513,514],{"class":472},"30",[453,516,517],{"class":458}," # Keep this to the max size the VM allows (this is the max of Standard_B2s)\n",[453,519,521],{"class":455,"line":520},7,[453,522,523],{"emptyLinePlaceholder":245},"\n",[453,525,527],{"class":455,"line":526},8,[453,528,529],{"class":458},"# Create the resource group\n",[453,531,533,537,540,543,547,550,553],{"class":455,"line":532},9,[453,534,536],{"class":535},"sScJk","az",[453,538,539],{"class":472}," group",[453,541,542],{"class":472}," create",[453,544,546],{"class":545},"sj4cs"," --name",[453,548,549],{"class":464}," $RESOURCEGROUP ",[453,551,552],{"class":545},"--location",[453,554,555],{"class":464}," $LOCATION\n",[453,557,558],{"class":455,"line":41},[453,559,523],{"emptyLinePlaceholder":245},[453,561,563],{"class":455,"line":562},11,[453,564,565],{"class":458},"# Create the AKS cluster - with the Standard Loadbalancer\n",[453,567,569,571,574,576,579,581,584,587],{"class":455,"line":568},12,[453,570,536],{"class":535},[453,572,573],{"class":472}," aks",[453,575,542],{"class":472},[453,577,578],{"class":545}," --resource-group",[453,580,549],{"class":464},[453,582,583],{"class":545},"--name",[453,585,586],{"class":464}," $CLUSTERNAME ",[453,588,589],{"class":545},"\\\n",[453,591,593,596,599],{"class":455,"line":592},13,[453,594,595],{"class":545},"  --node-vm-size",[453,597,598],{"class":464}," $VMSIZE ",[453,600,589],{"class":545},[453,602,604,607,610],{"class":455,"line":603},14,[453,605,606],{"class":545},"  --node-count",[453,608,609],{"class":545}," 1",[453,611,612],{"class":545}," \\\n",[453,614,616,619,622,625],{"class":455,"line":615},15,[453,617,618],{"class":545},"  --node-resource-group",[453,620,621],{"class":464}," ${RESOURCEGROUP}",[453,623,624],{"class":472},"-nodes",[453,626,612],{"class":545},[453,628,630,633,636],{"class":455,"line":629},16,[453,631,632],{"class":545},"  --load-balancer-sku",[453,634,635],{"class":472}," standard",[453,637,612],{"class":545},[453,639,641,644],{"class":455,"line":640},17,[453,642,643],{"class":545},"  --generate-ssh-keys",[453,645,612],{"class":545},[453,647,649,652,655],{"class":455,"line":648},18,[453,650,651],{"class":545},"  --location",[453,653,654],{"class":464}," $LOCATION ",[453,656,589],{"class":545},[453,658,660,663,666],{"class":455,"line":659},19,[453,661,662],{"class":545},"  --node-osdisk-type",[453,664,665],{"class":472}," Ephemeral",[453,667,612],{"class":545},[453,669,670,673],{"class":455,"line":55},[453,671,672],{"class":545},"  --node-osdisk-size",[453,674,675],{"class":464}," $VMDISKSIZE\n",[453,677,679],{"class":455,"line":678},21,[453,680,523],{"emptyLinePlaceholder":245},[453,682,684],{"class":455,"line":683},22,[453,685,686],{"class":458},"# Get the credentials for the AKS cluster (for the following steps)\n",[453,688,690,692,694,697,699,701,703],{"class":455,"line":689},23,[453,691,536],{"class":535},[453,693,573],{"class":472},[453,695,696],{"class":472}," get-credentials",[453,698,578],{"class":545},[453,700,549],{"class":464},[453,702,583],{"class":545},[453,704,705],{"class":464}," $CLUSTERNAME\n",[231,707,708,709,712,713,716,717,719],{},"With the cluster up and running, the next step is to create a ",[312,710,711],{},"ClusterIssuer",", which will automate the issuance of ",[312,714,715],{},"TLS certificates"," using\n",[312,718,396],{}," and to set up an Ingress that will handle external traffic and route it to the correct service within the cluster.",[440,721,722],{},[443,723,728],{"className":724,"code":725,"filename":726,"language":727,"meta":21,"style":21},"language-bash shiki shiki-themes github-light github-dark","#bash\nLETSENCRYPTEMAIL=your@email.com # \u003C--- Change this to your email address\n\n# Create the letsencrypt cluster issuers\nhelm repo add jetstack https:\u002F\u002Fcharts.jetstack.io\nhelm repo update\nhelm install cert-manager jetstack\u002Fcert-manager \\\n  --namespace cert-manager \\\n  --create-namespace \\\n  --set crds.enabled=true\nkubectl create -f - \u003C\u003CEOF\n---\napiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt-production\n  namespace: cert-manager\nspec:\n  acme:\n    # The ACME server URL\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n    # Email address used for ACME registration\n    email: ${LETSENCRYPTEMAIL}\n    # Name of a secret used to store the ACME account private key\n    privateKeySecretRef:\n      name: letsencrypt-production\n    # Enable the HTTP-01 challenge provider\n    solvers:\n    - http01:\n        ingress:\n          class: nginx\nEOF\n\n# Install nginx ingress controller\nhelm repo add ingress-nginx https:\u002F\u002Fkubernetes.github.io\u002Fingress-nginx\nhelm repo update\nhelm upgrade --install \\\n  ingress-nginx ingress-nginx\u002Fingress-nginx \\\n  --namespace ingress-nginx \\\n  --set controller.service.type=LoadBalancer \\\n  --set controller.service.annotations.\"service\\.beta\\.kubernetes\\.io\u002Fazure-load-balancer-health-probe-request-path\"=\u002Fhealthz \\\n  --set controller.service.externalTrafficPolicy=Local \\\n  --create-namespace\n\necho \"Waiting for the external ip to be assigned to the ingress controller, once it is available you can stop this script\"\nkubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch\n","Configure Ingress and TLS certificates","bash",[450,729,730,734,747,751,756,773,782,797,806,813,824,843,848,853,858,863,868,873,878,883,888,893,898,908,914,920,926,932,938,944,949,955,959,964,970,985,994,1007,1018,1027,1037,1046,1056,1062,1067,1076],{"__ignoreMap":21},[453,731,732],{"class":455,"line":20},[453,733,459],{"class":458},[453,735,736,739,741,744],{"class":455,"line":131},[453,737,738],{"class":464},"LETSENCRYPTEMAIL",[453,740,469],{"class":468},[453,742,743],{"class":472},"your@email.com",[453,745,746],{"class":458}," # \u003C--- Change this to your email address\n",[453,748,749],{"class":455,"line":155},[453,750,523],{"emptyLinePlaceholder":245},[453,752,753],{"class":455,"line":180},[453,754,755],{"class":458},"# Create the letsencrypt cluster issuers\n",[453,757,758,761,764,767,770],{"class":455,"line":204},[453,759,760],{"class":535},"helm",[453,762,763],{"class":472}," repo",[453,765,766],{"class":472}," add",[453,768,769],{"class":472}," jetstack",[453,771,772],{"class":472}," https:\u002F\u002Fcharts.jetstack.io\n",[453,774,775,777,779],{"class":455,"line":506},[453,776,760],{"class":535},[453,778,763],{"class":472},[453,780,781],{"class":472}," update\n",[453,783,784,786,789,792,795],{"class":455,"line":520},[453,785,760],{"class":535},[453,787,788],{"class":472}," install",[453,790,791],{"class":472}," cert-manager",[453,793,794],{"class":472}," jetstack\u002Fcert-manager",[453,796,612],{"class":545},[453,798,799,802,804],{"class":455,"line":526},[453,800,801],{"class":545},"  --namespace",[453,803,791],{"class":472},[453,805,612],{"class":545},[453,807,808,811],{"class":455,"line":532},[453,809,810],{"class":545},"  --create-namespace",[453,812,612],{"class":545},[453,814,815,818,821],{"class":455,"line":41},[453,816,817],{"class":545},"  --set",[453,819,820],{"class":472}," crds.enabled=",[453,822,823],{"class":545},"true\n",[453,825,826,829,831,834,837,840],{"class":455,"line":562},[453,827,828],{"class":535},"kubectl",[453,830,542],{"class":472},[453,832,833],{"class":545}," -f",[453,835,836],{"class":472}," -",[453,838,839],{"class":468}," \u003C\u003C",[453,841,842],{"class":472},"EOF\n",[453,844,845],{"class":455,"line":568},[453,846,847],{"class":472},"---\n",[453,849,850],{"class":455,"line":592},[453,851,852],{"class":472},"apiVersion: cert-manager.io\u002Fv1\n",[453,854,855],{"class":455,"line":603},[453,856,857],{"class":472},"kind: ClusterIssuer\n",[453,859,860],{"class":455,"line":615},[453,861,862],{"class":472},"metadata:\n",[453,864,865],{"class":455,"line":629},[453,866,867],{"class":472},"  name: letsencrypt-production\n",[453,869,870],{"class":455,"line":640},[453,871,872],{"class":472},"  namespace: cert-manager\n",[453,874,875],{"class":455,"line":648},[453,876,877],{"class":472},"spec:\n",[453,879,880],{"class":455,"line":659},[453,881,882],{"class":472},"  acme:\n",[453,884,885],{"class":455,"line":55},[453,886,887],{"class":472},"    # The ACME server URL\n",[453,889,890],{"class":455,"line":678},[453,891,892],{"class":472},"    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n",[453,894,895],{"class":455,"line":683},[453,896,897],{"class":472},"    # Email address used for ACME registration\n",[453,899,900,903,905],{"class":455,"line":689},[453,901,902],{"class":472},"    email: ${",[453,904,738],{"class":464},[453,906,907],{"class":472},"}\n",[453,909,911],{"class":455,"line":910},24,[453,912,913],{"class":472},"    # Name of a secret used to store the ACME account private key\n",[453,915,917],{"class":455,"line":916},25,[453,918,919],{"class":472},"    privateKeySecretRef:\n",[453,921,923],{"class":455,"line":922},26,[453,924,925],{"class":472},"      name: letsencrypt-production\n",[453,927,929],{"class":455,"line":928},27,[453,930,931],{"class":472},"    # Enable the HTTP-01 challenge provider\n",[453,933,935],{"class":455,"line":934},28,[453,936,937],{"class":472},"    solvers:\n",[453,939,941],{"class":455,"line":940},29,[453,942,943],{"class":472},"    - http01:\n",[453,945,946],{"class":455,"line":69},[453,947,948],{"class":472},"        ingress:\n",[453,950,952],{"class":455,"line":951},31,[453,953,954],{"class":472},"          class: nginx\n",[453,956,957],{"class":455,"line":244},[453,958,842],{"class":472},[453,960,962],{"class":455,"line":961},33,[453,963,523],{"emptyLinePlaceholder":245},[453,965,967],{"class":455,"line":966},34,[453,968,969],{"class":458},"# Install nginx ingress controller\n",[453,971,973,975,977,979,982],{"class":455,"line":972},35,[453,974,760],{"class":535},[453,976,763],{"class":472},[453,978,766],{"class":472},[453,980,981],{"class":472}," ingress-nginx",[453,983,984],{"class":472}," https:\u002F\u002Fkubernetes.github.io\u002Fingress-nginx\n",[453,986,988,990,992],{"class":455,"line":987},36,[453,989,760],{"class":535},[453,991,763],{"class":472},[453,993,781],{"class":472},[453,995,997,999,1002,1005],{"class":455,"line":996},37,[453,998,760],{"class":535},[453,1000,1001],{"class":472}," upgrade",[453,1003,1004],{"class":545}," --install",[453,1006,612],{"class":545},[453,1008,1010,1013,1016],{"class":455,"line":1009},38,[453,1011,1012],{"class":472},"  ingress-nginx",[453,1014,1015],{"class":472}," ingress-nginx\u002Fingress-nginx",[453,1017,612],{"class":545},[453,1019,1021,1023,1025],{"class":455,"line":1020},39,[453,1022,801],{"class":545},[453,1024,981],{"class":472},[453,1026,612],{"class":545},[453,1028,1030,1032,1035],{"class":455,"line":1029},40,[453,1031,817],{"class":545},[453,1033,1034],{"class":472}," controller.service.type=LoadBalancer",[453,1036,612],{"class":545},[453,1038,1039,1041,1044],{"class":455,"line":265},[453,1040,817],{"class":545},[453,1042,1043],{"class":472}," controller.service.annotations.\"service\\.beta\\.kubernetes\\.io\u002Fazure-load-balancer-health-probe-request-path\"=\u002Fhealthz",[453,1045,612],{"class":545},[453,1047,1049,1051,1054],{"class":455,"line":1048},42,[453,1050,817],{"class":545},[453,1052,1053],{"class":472}," controller.service.externalTrafficPolicy=Local",[453,1055,612],{"class":545},[453,1057,1059],{"class":455,"line":1058},43,[453,1060,1061],{"class":545},"  --create-namespace\n",[453,1063,1065],{"class":455,"line":1064},44,[453,1066,523],{"emptyLinePlaceholder":245},[453,1068,1070,1073],{"class":455,"line":1069},45,[453,1071,1072],{"class":545},"echo",[453,1074,1075],{"class":472}," \"Waiting for the external ip to be assigned to the ingress controller, once it is available you can stop this script\"\n",[453,1077,1079,1081,1084,1087,1090,1092,1095,1098,1101],{"class":455,"line":1078},46,[453,1080,828],{"class":535},[453,1082,1083],{"class":472}," get",[453,1085,1086],{"class":472}," service",[453,1088,1089],{"class":545}," --namespace",[453,1091,981],{"class":472},[453,1093,1094],{"class":472}," ingress-nginx-controller",[453,1096,1097],{"class":545}," --output",[453,1099,1100],{"class":472}," wide",[453,1102,1103],{"class":545}," --watch\n",[231,1105,1106,1107,1110],{},"Wait for the ",[312,1108,1109],{},"external IP"," to become available. Once it appears, you can stop the script -\nyour cluster is now fully set up to run services and expose them via that public IP address.",[231,1112,1113,1114,1117,1118,1121,1122,1125],{},"Next, head over to your ",[312,1115,1116],{},"DNS provider"," and create a ",[312,1119,1120],{},"DNS record"," pointing to this IP.\nFor example, you could create a subdomain like ",[450,1123,1124],{},"plausible.yourdomain.com",".",[231,1127,1128,1129,1132],{},"Now we're going to ",[312,1130,1131],{},"install Plausible"," on the cluster. In the configuration below, you'll see that we:",[383,1134,1135,1141,1150],{},[386,1136,1137,1140],{},[312,1138,1139],{},"Persist the storage"," for both PostgreSQL and ClickHouse - this is where all your analytics data will be stored. This way you can\neasily back up your data and restore it if needed. It also allows the containers to be managed without losing data.",[386,1142,1143,1146,1147,1149],{},[312,1144,1145],{},"Annotate the Ingress"," with the ",[312,1148,711],{}," we created earlier, so that it can automatically request and renew TLS certificates for us.",[386,1151,1152,1155],{},[312,1153,1154],{},"Connect Plausible to the Ingress",", and specify the DNS name for public access.",[440,1157,1158],{},[443,1159,1162],{"className":724,"code":1160,"filename":1161,"language":727,"meta":21,"style":21},"#bash\nPLAUSIBLE_DNS=plausible.yourdomain.com\n\n# Deploy Plausible Analytics using Helm\nhelm repo add imio https:\u002F\u002Fimio.github.io\u002Fhelm-charts\nhelm repo update\nhelm upgrade --install plausible-analytics imio\u002Fplausible-analytics \\\n  --namespace plausible-analytics \\\n  --create-namespace \\\n  --version 0.4.2 \\\n  --set baseURL=\"http:\u002F\u002F${PLAUSIBLE_DNS}\" \\\n  --set postgresql.primary.persistence.enabled=true \\\n  --set postgresql.primary.persistence.size=1Gi \\\n  --set clickhouse.persistence.enabled=true \\\n  --set clickhouse.persistence.size=8Gi \\\n  --set ingress.enabled=true \\\n  --set ingress.annotations.\"cert-manager\\.io\u002Fcluster-issuer\"=\"letsencrypt-production\" \\\n  --set ingress.annotations.\"kubernetes\\.io\u002Fingress\\.class\"=nginx \\\n  --set ingress.annotations.\"kubernetes\\.io\u002Ftls-acme\"=\"\\\"true\\\"\" \\\n  --set ingress.className=nginx \\\n  --set ingress.hosts[0]=${PLAUSIBLE_DNS} \\\n  --set ingress.paths.path=\u002F \\\n  --set ingress.tls[0].secretName=letsencrypt-production \\\n  --set ingress.tls[0].hosts[0]=${PLAUSIBLE_DNS}\n\n# Monitor deployment progress\necho \"Monitoring deployment progress...\"\nkubectl get pods -n plausible-analytics -w\n\n","CloudShell",[450,1163,1164,1168,1178,1182,1187,1201,1209,1225,1233,1239,1249,1263,1275,1284,1295,1304,1315,1324,1333,1352,1361,1373,1382,1391,1401,1405,1410,1417],{"__ignoreMap":21},[453,1165,1166],{"class":455,"line":20},[453,1167,459],{"class":458},[453,1169,1170,1173,1175],{"class":455,"line":131},[453,1171,1172],{"class":464},"PLAUSIBLE_DNS",[453,1174,469],{"class":468},[453,1176,1177],{"class":472},"plausible.yourdomain.com\n",[453,1179,1180],{"class":455,"line":155},[453,1181,523],{"emptyLinePlaceholder":245},[453,1183,1184],{"class":455,"line":180},[453,1185,1186],{"class":458},"# Deploy Plausible Analytics using Helm\n",[453,1188,1189,1191,1193,1195,1198],{"class":455,"line":204},[453,1190,760],{"class":535},[453,1192,763],{"class":472},[453,1194,766],{"class":472},[453,1196,1197],{"class":472}," imio",[453,1199,1200],{"class":472}," https:\u002F\u002Fimio.github.io\u002Fhelm-charts\n",[453,1202,1203,1205,1207],{"class":455,"line":506},[453,1204,760],{"class":535},[453,1206,763],{"class":472},[453,1208,781],{"class":472},[453,1210,1211,1213,1215,1217,1220,1223],{"class":455,"line":520},[453,1212,760],{"class":535},[453,1214,1001],{"class":472},[453,1216,1004],{"class":545},[453,1218,1219],{"class":472}," plausible-analytics",[453,1221,1222],{"class":472}," imio\u002Fplausible-analytics",[453,1224,612],{"class":545},[453,1226,1227,1229,1231],{"class":455,"line":526},[453,1228,801],{"class":545},[453,1230,1219],{"class":472},[453,1232,612],{"class":545},[453,1234,1235,1237],{"class":455,"line":532},[453,1236,810],{"class":545},[453,1238,612],{"class":545},[453,1240,1241,1244,1247],{"class":455,"line":41},[453,1242,1243],{"class":545},"  --version",[453,1245,1246],{"class":545}," 0.4.2",[453,1248,612],{"class":545},[453,1250,1251,1253,1256,1258,1261],{"class":455,"line":562},[453,1252,817],{"class":545},[453,1254,1255],{"class":472}," baseURL=\"http:\u002F\u002F${",[453,1257,1172],{"class":464},[453,1259,1260],{"class":472},"}\"",[453,1262,612],{"class":545},[453,1264,1265,1267,1270,1273],{"class":455,"line":568},[453,1266,817],{"class":545},[453,1268,1269],{"class":472}," postgresql.primary.persistence.enabled=",[453,1271,1272],{"class":545},"true",[453,1274,612],{"class":545},[453,1276,1277,1279,1282],{"class":455,"line":592},[453,1278,817],{"class":545},[453,1280,1281],{"class":472}," postgresql.primary.persistence.size=1Gi",[453,1283,612],{"class":545},[453,1285,1286,1288,1291,1293],{"class":455,"line":603},[453,1287,817],{"class":545},[453,1289,1290],{"class":472}," clickhouse.persistence.enabled=",[453,1292,1272],{"class":545},[453,1294,612],{"class":545},[453,1296,1297,1299,1302],{"class":455,"line":615},[453,1298,817],{"class":545},[453,1300,1301],{"class":472}," clickhouse.persistence.size=8Gi",[453,1303,612],{"class":545},[453,1305,1306,1308,1311,1313],{"class":455,"line":629},[453,1307,817],{"class":545},[453,1309,1310],{"class":472}," ingress.enabled=",[453,1312,1272],{"class":545},[453,1314,612],{"class":545},[453,1316,1317,1319,1322],{"class":455,"line":640},[453,1318,817],{"class":545},[453,1320,1321],{"class":472}," ingress.annotations.\"cert-manager\\.io\u002Fcluster-issuer\"=\"letsencrypt-production\"",[453,1323,612],{"class":545},[453,1325,1326,1328,1331],{"class":455,"line":648},[453,1327,817],{"class":545},[453,1329,1330],{"class":472}," ingress.annotations.\"kubernetes\\.io\u002Fingress\\.class\"=nginx",[453,1332,612],{"class":545},[453,1334,1335,1337,1340,1343,1345,1347,1350],{"class":455,"line":659},[453,1336,817],{"class":545},[453,1338,1339],{"class":472}," ingress.annotations.\"kubernetes\\.io\u002Ftls-acme\"=\"",[453,1341,1342],{"class":545},"\\\"",[453,1344,1272],{"class":472},[453,1346,1342],{"class":545},[453,1348,1349],{"class":472},"\"",[453,1351,612],{"class":545},[453,1353,1354,1356,1359],{"class":455,"line":55},[453,1355,817],{"class":545},[453,1357,1358],{"class":472}," ingress.className=nginx",[453,1360,612],{"class":545},[453,1362,1363,1365,1368,1371],{"class":455,"line":678},[453,1364,817],{"class":545},[453,1366,1367],{"class":472}," ingress.hosts[0]=",[453,1369,1370],{"class":464},"${PLAUSIBLE_DNS} ",[453,1372,589],{"class":545},[453,1374,1375,1377,1380],{"class":455,"line":683},[453,1376,817],{"class":545},[453,1378,1379],{"class":472}," ingress.paths.path=\u002F",[453,1381,612],{"class":545},[453,1383,1384,1386,1389],{"class":455,"line":689},[453,1385,817],{"class":545},[453,1387,1388],{"class":472}," ingress.tls[0].secretName=letsencrypt-production",[453,1390,612],{"class":545},[453,1392,1393,1395,1398],{"class":455,"line":910},[453,1394,817],{"class":545},[453,1396,1397],{"class":472}," ingress.tls[0].hosts[0]=",[453,1399,1400],{"class":464},"${PLAUSIBLE_DNS}\n",[453,1402,1403],{"class":455,"line":916},[453,1404,523],{"emptyLinePlaceholder":245},[453,1406,1407],{"class":455,"line":922},[453,1408,1409],{"class":458},"# Monitor deployment progress\n",[453,1411,1412,1414],{"class":455,"line":928},[453,1413,1072],{"class":545},[453,1415,1416],{"class":472}," \"Monitoring deployment progress...\"\n",[453,1418,1419,1421,1423,1426,1429,1431],{"class":455,"line":934},[453,1420,828],{"class":535},[453,1422,1083],{"class":472},[453,1424,1425],{"class":472}," pods",[453,1427,1428],{"class":545}," -n",[453,1430,1219],{"class":472},[453,1432,1433],{"class":545}," -w\n",[231,1435,1436,1437,1440,1441,1125],{},"After the script finishes, you should be able to access ",[312,1438,1439],{},"Plausible"," at the DNS name you configured - for example,\n",[450,1442,1124],{},[374,1444],{"alt":1445,"caption":1445,"css-class":1446,"src":1447},"Plausible.io community setup screen","aspect-[735\u002F580]","posts\u002F2\u002Fplausible-installed.jpg",[326,1449,1451],{"id":1450},"conclusion","Conclusion",[231,1453,1454,1455,1458,1459,1461,1462,1465,1466,1469,1470,1473,1474,1477,1478,1481],{},"In this article, we've set up a ",[312,1456,1457],{},"cost-effective and privacy-friendly analytics solution"," using ",[312,1460,408],{}," on\n",[312,1463,1464],{},"Azure Kubernetes Service (AKS)",".\nBy leveraging Kubernetes, we're able to ",[312,1467,1468],{},"scale",", ",[312,1471,1472],{},"manage",", and ",[312,1475,1476],{},"secure"," our analytics platform while staying compliant with\nprivacy regulations - all ",[312,1479,1480],{},"without"," annoying cookie consent banners.",[231,1483,1484],{},"You learned how to:",[383,1486,1487,1497,1508],{},[386,1488,1489,1490,1493,1494],{},"Configure the ",[312,1491,1492],{},"cheapest possible AKS cluster"," using your ",[312,1495,1496],{},"Azure credits",[386,1498,1499,1500,1503,1504,1507],{},"Set up ",[312,1501,1502],{},"Ingress"," and ",[312,1505,1506],{},"TLS certificate automation"," using cert-manager and Let's Encrypt",[386,1509,1510,1511,1513],{},"Deploy and run ",[312,1512,408],{}," with persistent storage for both PostgreSQL and ClickHouse",[231,1515,1516,1517,1520],{},"This setup provides ",[312,1518,1519],{},"valuable insights into website traffic",", respects user privacy,\nand gives you full control over your data - all at a low monthly cost.",[231,1522,1523,1524,1469,1527,1530,1531,1534],{},"If you're looking for a ",[312,1525,1526],{},"lightweight",[312,1528,1529],{},"open-source alternative"," to ",[312,1532,1533],{},"Google Analytics",",\nPlausible.io is definitely worth considering.",[231,1536,1537],{},"If you have any questions or need help along the way, feel free to reach out.",[231,1539,1540],{},[312,1541,1542],{},"Happy tracking!",[1544,1545,1546],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":21,"searchDepth":131,"depth":131,"links":1548},[1549,1550,1551,1552],{"id":328,"depth":131,"text":329},{"id":348,"depth":131,"text":349},{"id":358,"depth":131,"text":359},{"id":1450,"depth":131,"text":1451},"Kubernetes","2025-04-06T16:11:24","Learn how to setup an Azure Kubernetes Cluster, running Plausible.io. An open-source, lightweight and privacy-focuses analytics tool. Which complies with the EU GDPR and doesn't require you to show a cookie consent banner.",{"type":228,"value":1557},[1558],[231,1559,294],{},"Plausible.io, Kubernetes and Azure logos","object-top","\u002Fposts\u002F2\u002Fcover.jpeg",[1553,1564,408],"Azure",{},{"text":1567,"minutes":1568,"time":1569,"words":1570},"7 min read",6.16,369600,1232,{"title":128,"description":1555},"q1WY6p5imKJJBwvU7PPFnGQU_9t_O7AFwVxba_fy2J0",{"id":272,"company":1574,"extension":277,"fullName":278,"github":279,"homePage":280,"imageUrl":276,"linkedIn":281,"meta":1575,"role":283,"stem":284,"twitter":285,"userName":286,"__hash__":287},{"name":274,"url":275,"imageUrl":276},{},1781641709303]