{"id":56,"date":"2025-06-14T22:01:00","date_gmt":"2025-06-15T02:01:00","guid":{"rendered":"https:\/\/www.varchitected.com\/?p=56"},"modified":"2025-08-20T18:03:53","modified_gmt":"2025-08-20T22:03:53","slug":"%f0%9f%8e%af-deploying-wordpress-on-gke-with-click-to-deploy-secured-styled-and-surviving-my-sanity","status":"publish","type":"post","link":"https:\/\/varchitected.com\/?p=56","title":{"rendered":"\ud83c\udfaf Deploying WordPress on GKE with Click-to-Deploy: Secured, Styled, and Surviving My Sanity"},"content":{"rendered":"\n<p>Let me paint a picture.<\/p>\n\n\n\n<p>You&#8217;re sipping your morning cold brew, dreaming of a WordPress site served fresh from Kubernetes. \u201cClick-to-Deploy,\u201d you say smugly. \u201cHow hard could it be?\u201d<\/p>\n\n\n\n<p>Fast forward 2 hours later &#8211; you\u2019re SSH\u2019d into a container, your site is displaying the WordPress default, and your beautiful backend is wide open like it\u2019s 2007.<\/p>\n\n\n\n<p>Welcome to the <strong>real<\/strong> journey of deploying WordPress on GKE with security and sanity in mind. Buckle up.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Step 1: Click-to-Deploy \u2013 It Actually Works (Sorta)<\/h2>\n\n\n\n<p>Google Cloud&#8217;s Click-to-Deploy makes it deceptively simple to launch a WordPress site on GKE. You choose your region, your cluster, and Google sets up:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A GKE deployment with pods running WordPress and MariaDB<\/li>\n\n\n\n<li>A LoadBalancer service for external access<\/li>\n\n\n\n<li>Persistent Disks for data durability<\/li>\n\n\n\n<li>An Ingress controller for routing<\/li>\n\n\n\n<li>A managed SSL certificate for your domain (automatic renewal FTW \ud83d\ude4c)<\/li>\n<\/ul>\n\n\n\n<p>But that\u2019s where the <em>real<\/em> work starts.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd10 Step 2: Locking Down <code>\/wp-admin<\/code> Like It&#8217;s Area 51<\/h2>\n\n\n\n<p>By default, <code>\/wp-admin<\/code> is public-facing. This is fine if you enjoy brute force attempts from random IPs in places you can&#8217;t pronounce.<\/p>\n\n\n\n<p>Here\u2019s how I fixed that using <strong>Cloud Armor<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code># cloud-armor-policy.yaml\n- action: \"deny(403)\"\n  description: \"Block access to \/wp-admin from non-approved IPs\"\n  match:\n    expr:\n      expression: 'request.path.startsWith(\"\/wp-admin\") &amp;&amp; ip != \"YOUR_IP\"'\n<\/code><\/code><\/pre>\n\n\n\n<p>\ud83d\udd27 This policy was applied to the backend service behind the Ingress. Now only my IP gets in. Everyone else? \ud83d\udc4b Denied.<\/p>\n\n\n\n<p>And if you&#8217;re wondering why it wasn\u2019t working the first time? Google Cloud Armor policies must be explicitly attached to the backend service and <strong>precedence matters<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udee1\ufe0f Step 3: Multi-Factor Like a Boss \u2013 DUO MFA Plugin<\/h2>\n\n\n\n<p>I wasn\u2019t about to trust my WordPress login to a simple password, so I added <strong>DUO Security<\/strong> using their <strong>official WordPress plugin<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Steps:<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Created a new application in the DUO admin panel.<\/li>\n\n\n\n<li>Installed the DUO WordPress plugin via <code>wp-admin<\/code>.<\/li>\n\n\n\n<li>Configured the plugin with my DUO integration key, secret key, and API hostname.<\/li>\n<\/ol>\n\n\n\n<p>\ud83d\udc68\u200d\ud83d\udcbb This plugin integrates natively with WordPress login, prompting for second-factor auth directly. It works beautifully and adds minimal delay.<\/p>\n\n\n\n<p>Bonus: It doesn\u2019t interfere with wp-cli or other automation &#8211; unless you <em>want<\/em> it to.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddea Step 4: Debugging \u2013 The Unexpected \u201cDefault\u201d WordPress Reset<\/h2>\n\n\n\n<p>At one point, I thought my GKE cluster had lost its mind. My site reverted to default &#8211; vanilla WordPress, theme and all. Turns out&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cause:<\/h3>\n\n\n\n<p>GKE\u2019s rolling updates during auto-upgrade <strong>recycled the pods<\/strong>, but <strong>PVC mounts weren\u2019t persistent<\/strong> in the way I assumed. Classic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fix:<\/h3>\n\n\n\n<p>I moved my important data to a separate PersistentVolumeClaim, mounted explicitly via <code>volumeMounts<\/code> in the deployment manifest:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>volumeMounts:\n- mountPath: \/home\/u994648506\/domains\/varchitected.com\/public_html\/wp-content\n  name: wp-content-pvc\n<\/code><\/code><\/pre>\n\n\n\n<p>And yes, I now back up the database regularly. Lesson learned.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf10 Step 5: SSL Certificates \u2013 The Lazy Man\u2019s Flex<\/h2>\n\n\n\n<p>Google Cloud\u2019s <strong>Managed Certificates<\/strong> are magic. Just apply them to your Ingress and they\u2019ll:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Auto-validate your domain<\/li>\n\n\n\n<li>Handle renewal<\/li>\n\n\n\n<li>Apply the cert to your Load Balancer automagically<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code><code>apiVersion: networking.gke.io\/v1\nkind: ManagedCertificate\nmetadata:\n  name: varchitected-cert\nspec:\n  domains:\n    - www.varchitected.com\n<\/code><\/code><\/pre>\n\n\n\n<p>Tie it to your ingress with an annotation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>metadata:\n  annotations:\n    networking.gke.io\/managed-certificates: \"varchitected-cert\"\n<\/code><\/code><\/pre>\n\n\n\n<p>Voila &#8211; HTTPS without breaking a sweat.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udca1 Final Tips: What I\u2019d Do Differently<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Store MariaDB externally<\/strong> &#8211; GKE restarts can get dicey. Cloud SQL or AlloyDB might save you from a panic attack.<\/li>\n\n\n\n<li><strong>Add <code>readinessProbes<\/code><\/strong> to your pods &#8211; it avoids weird 502s during rolling updates.<\/li>\n\n\n\n<li><strong>Get serious with backups<\/strong> &#8211; automate snapshots and export your database regularly.<\/li>\n\n\n\n<li><strong>Use a staging environment<\/strong> &#8211; Kubernetes is predictable\u2026 until it isn\u2019t.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfa4 Final Thoughts<\/h2>\n\n\n\n<p>Running WordPress on GKE is a flex, but like any good lab project, it\u2019ll humble you. When done right, though? You get a secure, auto-scaling, SSL-enabled, MFA-protected WordPress site &#8211; with the full power of Kubernetes at your fingertips.<\/p>\n\n\n\n<p>And more importantly, you earn the right to say:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Yeah, I run my blog on GKE. No big deal.&#8221;<\/p>\n<\/blockquote>\n\n\n\n<p>What&#8217;s next in GKE? Backups, PVC Snapshots, etc.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let me paint a picture. You&#8217;re sipping your morning cold brew, dreaming of a WordPress site served fresh from Kubernetes. \u201cClick-to-Deploy,\u201d you say smugly. \u201cHow hard could it be?\u201d Fast forward 2 hours later &#8211; you\u2019re SSH\u2019d into a container, your site is displaying the WordPress default, and your beautiful backend is wide open like [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,17,6],"tags":[],"class_list":["post-56","post","type-post","status-publish","format-standard","hentry","category-google","category-google-cloud-armor","category-gke"],"_links":{"self":[{"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/posts\/56","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/varchitected.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=56"}],"version-history":[{"count":1,"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions"}],"predecessor-version":[{"id":57,"href":"https:\/\/varchitected.com\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions\/57"}],"wp:attachment":[{"href":"https:\/\/varchitected.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=56"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/varchitected.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=56"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/varchitected.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=56"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}