Add Docker/Traefik deployment setup

This commit is contained in:
2026-08-01 16:38:15 +02:00
parent c4c750cb74
commit e04495ee2d
7 changed files with 79 additions and 3 deletions
+7
View File
@@ -0,0 +1,7 @@
node_modules
.next
.git
.env*
!.env.example
design
*.md
+12
View File
@@ -0,0 +1,12 @@
# Used by docker-compose.yml for the Traefik Host() rule
DOMAIN=
GMAIL_USER=
GMAIL_APP_PASSWORD=
CONTACT_TO_EMAIL=
# Private pricing pages — JSON array, one object per page, reached at
# /<locale>/plans/<code>. `landing`/`webApp`/`care` take a number (rendered as
# €N, formatted per locale) or a string (used verbatim). Add or remove objects
# to add or remove pages. Generate an unguessable code with: openssl rand -hex 8
PLANS_PAGES=[{"code":"REPLACE_WITH_RANDOM_HEX","landing":500,"webApp":2000,"care":[25,60,120]}]
+2 -1
View File
@@ -33,8 +33,9 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
# env files
.env*
!.env.example
# vercel
.vercel
+30
View File
@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
# The /plans/[code] route uses generateStaticParams + dynamicParams = false,
# so PLANS_PAGES must be known at build time — codes not baked in here will
# 404 at runtime no matter what PLANS_PAGES is set to when the container runs.
ARG PLANS_PAGES
ENV PLANS_PAGES=$PLANS_PAGES
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
+2 -2
View File
@@ -23,14 +23,14 @@ export default function Footer({ locale, dict }: FooterProps) {
[dict.footer.connect]: [
{ label: "Source Code", href: "https://git.ksan.dev/ksan", external: true },
{ label: "Email", href: "mailto:contact@ksan.dev", external: true },
{ label: "LinkedIn", href: "https://linkedin.com", external: true },
{ label: "LinkedIn", href: "https://www.linkedin.com/in/%C4%91or%C4%91e-k%C5%A1an-886871266", external: true },
],
};
const socials = [
{ icon: SiGitea, href: "https://git.ksan.dev/ksan", label: "Gitea" },
{ icon: Mail, href: "mailto:contact@ksan.dev", label: "Email" },
{ icon: FaLinkedin, href: "https://linkedin.com", label: "LinkedIn" },
{ icon: FaLinkedin, href: "https://www.linkedin.com/in/%C4%91or%C4%91e-k%C5%A1an-886871266/", label: "LinkedIn" },
];
return (
+25
View File
@@ -0,0 +1,25 @@
services:
ksan-dev:
build:
context: .
args:
PLANS_PAGES: ${PLANS_PAGES}
container_name: ksan-dev
restart: unless-stopped
env_file:
- .env
networks:
- frontend
labels:
- traefik.enable=true
- traefik.http.routers.ksan-dev-http.rule=Host(`${DOMAIN}`) || Host(`www.${DOMAIN}`)
- traefik.http.routers.ksan-dev-http.entrypoints=web
- traefik.http.routers.ksan-dev-https.tls=true
- traefik.http.routers.ksan-dev-https.tls.certresolver=cloudflare
- traefik.http.routers.ksan-dev-https.entrypoints=websecure
- traefik.http.routers.ksan-dev-https.rule=Host(`${DOMAIN}`) || Host(`www.${DOMAIN}`)
- traefik.http.services.ksan-dev.loadbalancer.server.port=3000
networks:
frontend:
external: true
+1
View File
@@ -2,6 +2,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
allowedDevOrigins: ["192.168.100.6"],
output: "standalone",
};
export default nextConfig;