feat: adopt ghost-en into GitOps with non-www → www redirect

Brings the existing manually-applied ghost-en (theexclusionzone.com)
into the GitOps repo. Adds redirect-to-www-https middleware to fix the
Google Search Console redirect issue: non-www now 301s to https://www
instead of serving duplicate content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 08:59:20 +00:00
parent c50000a1e3
commit 9ab78d9bb6
6 changed files with 234 additions and 0 deletions
+1
View File
@@ -28,6 +28,7 @@ Los secrets TLS los gestiona **cert-manager** automáticamente a partir del recu
| `cloudflare-ddns` | `cloudflare-ddns-secret` | Ver Vaultwarden → "cloudflare-ddns" | | `cloudflare-ddns` | `cloudflare-ddns-secret` | Ver Vaultwarden → "cloudflare-ddns" |
| `vaultwarden` | `vaultwarden-secret` | Ver Vaultwarden → "vaultwarden" | | `vaultwarden` | `vaultwarden-secret` | Ver Vaultwarden → "vaultwarden" |
| `openclaw` | `openclaw-token` | Ver Vaultwarden → "openclaw" | | `openclaw` | `openclaw-token` | Ver Vaultwarden → "openclaw" |
| `ghost-en` | `ghost-en-smtp` | `kubectl create secret generic ghost-en-smtp --from-literal=SMTP_USER='<user-gmail>' --from-literal=SMTP_PASS='<app-password>' -n ghost-en` |
| `argocd` | `argocd-secret` | Gestionado por ArgoCD bootstrap | | `argocd` | `argocd-secret` | Gestionado por ArgoCD bootstrap |
| `argocd` | `argocd-redis` | Gestionado por ArgoCD bootstrap | | `argocd` | `argocd-redis` | Gestionado por ArgoCD bootstrap |
| `monitoring` | `kube-prometheus-stack-grafana` | Ver Vaultwarden → "grafana" | | `monitoring` | `kube-prometheus-stack-grafana` | Ver Vaultwarden → "grafana" |
+28
View File
@@ -0,0 +1,28 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ghost-en
namespace: argocd
spec:
project: default
source:
repoURL: https://git.chemavx.xyz/chemavx/k8s-manifests
targetRevision: HEAD
path: ghost-en
destination:
server: https://kubernetes.default.svc
namespace: ghost-en
ignoreDifferences:
- group: ""
kind: Secret
name: ghost-en-smtp
namespace: ghost-en
jsonPointers:
- /data
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- RespectIgnoreDifferences=true
+13
View File
@@ -0,0 +1,13 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ghost-en-tls
namespace: ghost-en
spec:
secretName: ghost-en-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
dnsNames:
- theexclusionzone.com
- www.theexclusionzone.com
+118
View File
@@ -0,0 +1,118 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: ghost-en
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ghost-en-content
namespace: ghost-en
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ghost-en
namespace: ghost-en
labels:
app: ghost-en
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: ghost-en
template:
metadata:
labels:
app: ghost-en
spec:
containers:
- name: ghost
image: ghost:5-alpine
ports:
- containerPort: 2368
env:
- name: url
value: "https://www.theexclusionzone.com"
- name: NODE_ENV
value: "production"
- name: database__client
value: "sqlite3"
- name: database__connection__filename
value: "/var/lib/ghost/content/data/ghost.db"
- name: mail__transport
value: "SMTP"
- name: mail__options__service
value: "Gmail"
- name: mail__options__host
value: "smtp.gmail.com"
- name: mail__options__port
value: "465"
- name: mail__options__secureConnection
value: "true"
- name: mail__options__auth__user
valueFrom:
secretKeyRef:
name: ghost-en-smtp
key: SMTP_USER
- name: mail__options__auth__pass
valueFrom:
secretKeyRef:
name: ghost-en-smtp
key: SMTP_PASS
- name: privacy__useUpdateCheck
value: "false"
livenessProbe:
httpGet:
path: /ghost/api/v4/admin/site/
port: 2368
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /ghost/api/v4/admin/site/
port: 2368
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
resources:
requests:
cpu: "50m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
volumeMounts:
- name: ghost-en-content
mountPath: /var/lib/ghost/content
volumes:
- name: ghost-en-content
persistentVolumeClaim:
claimName: ghost-en-content
---
apiVersion: v1
kind: Service
metadata:
name: ghost-en
namespace: ghost-en
spec:
selector:
app: ghost-en
ports:
- port: 2368
targetPort: 2368
protocol: TCP
+52
View File
@@ -0,0 +1,52 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ghost-en-https
namespace: ghost-en
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`www.theexclusionzone.com`)
services:
- name: ghost-en
port: 2368
- kind: Rule
match: Host(`theexclusionzone.com`)
middlewares:
- name: redirect-to-www-https
namespace: ghost-en
services:
- name: ghost-en
port: 2368
tls:
secretName: ghost-en-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ghost-en-http-redirect
namespace: ghost-en
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`www.theexclusionzone.com`)
middlewares:
- name: redirect-to-https
namespace: ghost-en
services:
- name: ghost-en
port: 2368
- kind: Rule
match: Host(`theexclusionzone.com`)
middlewares:
- name: redirect-to-www-https
namespace: ghost-en
services:
- name: ghost-en
port: 2368
+22
View File
@@ -0,0 +1,22 @@
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-to-https
namespace: ghost-en
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-to-www-https
namespace: ghost-en
spec:
redirectRegex:
regex: ^https?://theexclusionzone\.com/(.*)
replacement: https://www.theexclusionzone.com/${1}
permanent: true