From e6a023e14a00f418b4f155b1b9af0f80dae2df99 Mon Sep 17 00:00:00 2001 From: Falkan Date: Thu, 19 Mar 2026 00:42:31 -0400 Subject: [PATCH] chore: add Dockerfile and k8s deployment manifests --- .dockerignore | 3 ++ Dockerfile | 14 ++++++++++ k8s/deployment.yaml | 68 +++++++++++++++++++++++++++++++++++++++++++++ k8s/secrets.yaml | 16 +++++++++++ 4 files changed, 101 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 k8s/deployment.yaml create mode 100644 k8s/secrets.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..743004a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +data/ +.env +node_modules/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e9e2b82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM node:22-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:22-alpine +WORKDIR /app +COPY --from=builder /app/build ./build +COPY --from=builder /app/package*.json ./ +RUN npm ci --omit=dev +EXPOSE 3000 +CMD ["node", "build/index.js"] diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..c384448 --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: humor-units + labels: + app: humor-units +spec: + replicas: 1 + selector: + matchLabels: + app: humor-units + template: + metadata: + labels: + app: humor-units + spec: + containers: + - name: humor-units + image: ghcr.io/YOUR_ORG/humor-units:latest # replace with your registry + ports: + - containerPort: 3000 + env: + - name: PORT + value: "3000" + - name: HOST + value: "0.0.0.0" + - name: ORIGIN + value: "https://humorunits.com" # replace with your domain + envFrom: + - secretRef: + name: humor-units-secrets # SESSION_SECRET, PASSWORD_HASH + volumeMounts: + - name: data + mountPath: /app/data + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 500m + memory: 256Mi + readinessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 30 + volumes: + - name: data + persistentVolumeClaim: + claimName: humor-units-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: humor-units-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi diff --git a/k8s/secrets.yaml b/k8s/secrets.yaml new file mode 100644 index 0000000..e35cbcb --- /dev/null +++ b/k8s/secrets.yaml @@ -0,0 +1,16 @@ +# humor-units-secrets — do NOT commit real values +# Create with: +# kubectl create secret generic humor-units-secrets \ +# --from-literal=SESSION_SECRET= \ +# --from-literal=PASSWORD_HASH= +# +# Or apply this manifest after filling in base64-encoded values: +# echo -n 'value' | base64 +apiVersion: v1 +kind: Secret +metadata: + name: humor-units-secrets +type: Opaque +data: + SESSION_SECRET: "" # base64-encoded + PASSWORD_HASH: "" # base64-encoded bcrypt hash