쿠버네티스 클러스터에 Strapi 배포

작성자

카테고리:

← 피드로
DEV Community · Sanskriti Harmukh · 2026-07-24 개발(SW)

Strapi is a headless CMS with an intuitive admin dashboard, custom content types, and role-based permissions, well-suited to Kubernetes for its scalability and multi-environment support. This guide builds a Strapi app, containerizes it, and deploys it to Kubernetes behind an Nginx Ingress Controller with cert-manager TLS.

Prerequisites: a Kubernetes cluster with kubectl/helm configured, a PostgreSQL database, Docker on your workstation, a domain (this guide uses strapi.example.com) pointed at the ingress load balancer once provisioned.

Build the Strapi Application

1. Install Node.js:

$ curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -
$ sudo apt install -y nodejs

Enter fullscreen mode Exit fullscreen mode

2. Configure Git if not already set up:

$ git config --global user.name "Your Full Name"
$ git config --global user.email "[email protected]"

Enter fullscreen mode Exit fullscreen mode

3. Scaffold a project:

$ npx create-strapi-app@latest strapi-project --quickstart --js

Enter fullscreen mode Exit fullscreen mode

Once it’s running, press Ctrl+C to stop the dev server and continue configuring it.

Containerize the Application

1. Point it at PostgreSQL — edit .env in the project directory:

$ cd strapi-project
$ nano .env

Enter fullscreen mode Exit fullscreen mode

DATABASE_CLIENT=postgres
DATABASE_HOST=your-db-host
DATABASE_PORT=5432
DATABASE_NAME=strapi_db
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_SSL=true
DATABASE_SSL_REJECT_UNAUTHORIZED=false
DATABASE_SCHEMA=public
DATABASE_CONNECTION_TIMEOUT=60000

Enter fullscreen mode Exit fullscreen mode

2. Replace config/server.js:

$ mv config/server.js config/server.ORIG
$ nano config/server.js

Enter fullscreen mode Exit fullscreen mode

module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: env('STRAPI_URL'),
  app: {
    keys: env.array('APP_KEYS'),
  },
  webhooks: {
    populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
  },
});

Enter fullscreen mode Exit fullscreen mode

3. Set the URL, install the PostgreSQL driver, rebuild, run:

$ export STRAPI_URL="http://127.0.0.1:1337"
$ npm install pg --save
$ npm run build
$ npm run start &

Enter fullscreen mode Exit fullscreen mode

Visit http://<server-ip>:1337 and confirm the admin registration screen loads.

Build and Push the Docker Image

1. Dockerfile:

$ nano Dockerfile

Enter fullscreen mode Exit fullscreen mode

FROM node:22-alpine

RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /opt/
COPY package.json package-lock.json ./
RUN npm install
ENV PATH=/opt/node_modules/.bin:$PATH

WORKDIR /opt/app
COPY . .
RUN chown -R node:node /opt/app
USER node
RUN ["npm", "run", "build"]

EXPOSE 1337

CMD ["npm", "run", "start"]

Enter fullscreen mode Exit fullscreen mode

2. .dockerignore:

$ nano .dockerignore

Enter fullscreen mode Exit fullscreen mode

node_modules/

Enter fullscreen mode Exit fullscreen mode

3. Build, tag, push:

$ docker build -t strapi:latest .
$ docker login -u <username> -p <password>
$ docker tag strapi:latest <username>/strapi:latest
$ docker push <username>/strapi:latest

Enter fullscreen mode Exit fullscreen mode

Prepare the Cluster

Install the Nginx Ingress Controller

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update
$ kubectl create namespace ingress-nginx
$ helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx

Enter fullscreen mode Exit fullscreen mode

A LoadBalancer provisions automatically:

$ kubectl get services ingress-nginx-controller --namespace ingress-nginx

Enter fullscreen mode Exit fullscreen mode

It may take a few minutes for EXTERNAL-IP to populate.

Install cert-manager and a ClusterIssuer

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml

Enter fullscreen mode Exit fullscreen mode

Point your domain’s A record at the Ingress controller’s EXTERNAL-IP, then:

$ nano clusterissuer.yaml

Enter fullscreen mode Exit fullscreen mode

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: "[email protected]"
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx

Enter fullscreen mode Exit fullscreen mode

$ kubectl apply -f clusterissuer.yaml
$ kubectl get clusterissuer letsencrypt-prod

Enter fullscreen mode Exit fullscreen mode

READY: True confirms it’s live.

Deploy Strapi

1. Deployment (2 replicas):

$ nano strapi-deployment.yaml

Enter fullscreen mode Exit fullscreen mode

apiVersion: apps/v1
kind: Deployment
metadata:
  name: strapi-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      name: strapi-app
  template:
    metadata:
      labels:
        name: strapi-app
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: strapi
          image: <username>/strapi:latest
          imagePullPolicy: Always
          command: ["sh", "-c", "npm run build && npm run start"]
          ports:
            - containerPort: 1337
          env:
            - name: STRAPI_URL
              value: "strapi.example.com"

Enter fullscreen mode Exit fullscreen mode

regcred is a pull secret if your image lives in a private registry.

$ kubectl apply -f strapi-deployment.yaml
$ kubectl get deployment strapi-deployment

Enter fullscreen mode Exit fullscreen mode

2. Service:

$ nano strapi-service.yaml

Enter fullscreen mode Exit fullscreen mode

apiVersion: v1
kind: Service
metadata:
  name: strapi-service
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 1337
  selector:
    name: strapi-app

Enter fullscreen mode Exit fullscreen mode

$ kubectl apply -f strapi-service.yaml

Enter fullscreen mode Exit fullscreen mode

3. Ingress with TLS:

$ nano strapi-ingress.yaml

Enter fullscreen mode Exit fullscreen mode

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: strapi-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - secretName: strapi-tls
      hosts:
        - strapi.example.com
  rules:
    - host: strapi.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: strapi-service
                port:
                  number: 80

Enter fullscreen mode Exit fullscreen mode

$ kubectl apply -f strapi-ingress.yaml
$ kubectl get ingress

Enter fullscreen mode Exit fullscreen mode

Visit https://strapi.example.com — you should hit the login page over HTTPS.

Scale the Deployment

$ kubectl scale deployment/strapi-deployment --replicas=4
$ kubectl get deployment strapi-deployment
$ kubectl get pods

Enter fullscreen mode Exit fullscreen mode

Scale back down:

$ kubectl scale deployment/strapi-deployment --replicas=2

Enter fullscreen mode Exit fullscreen mode

Test fault tolerance by deleting a pod. Kubernetes replaces it and the Ingress routes around the gap with no downtime:

$ kubectl delete pod <pod-name>

Enter fullscreen mode Exit fullscreen mode

Next Steps

Strapi is running on Kubernetes with TLS termination and horizontal scaling. From here:

  • Add a HorizontalPodAutoscaler to scale on CPU/memory instead of manually
  • Move media uploads to S3-compatible object storage via a Strapi upload provider plugin
  • Set resource requests/limits on the Deployment to avoid noisy-neighbor scheduling issues

For the full guide, visit the original article on Vultr Docs.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다