deploy: publish site on host port 1337; nginx proxies /api/contact; auto-deploy
- site nginx listens on 1337; proxies /api/contact to the relay over the internal
network (single public port; TLS terminates upstream at Cloudflare)
- trailing-slash rewrite moved inside location / so /api/contact isn't redirected
- compose: publish ${SITE_PORT:-1337}, internal bridge network, no Traefik labels
(non-destructive — does not touch the WordPress apex router)
- .gitea/workflows/deploy.yml: on push to main, runner rebuilds + restarts the stack
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
# Auto-deploy on push to main. Runs on the self-hosted Gitea runner (which has the
|
||||||
|
# host Docker socket) and rebuilds + restarts the stack on the same host.
|
||||||
|
# The public site ends up on host port 1337 (see deploy/docker-compose.yml).
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Build and (re)start the stack
|
||||||
|
run: docker compose -f deploy/docker-compose.yml up -d --build --remove-orphans
|
||||||
+3
-4
@@ -14,7 +14,6 @@ RELAY_WINDOW_SECONDS=600
|
|||||||
# --- Site build ---
|
# --- Site build ---
|
||||||
PUBLIC_SITE_URL=https://cesnimda.co.uk
|
PUBLIC_SITE_URL=https://cesnimda.co.uk
|
||||||
|
|
||||||
# --- Infra (Traefik host reverse proxy) ---
|
# --- Infra ---
|
||||||
PROXY_NETWORK=traefik_proxy # existing external Traefik docker network
|
SITE_PORT=1337 # host port the public-facing site is published on
|
||||||
SITE_HOST=cesnimda.co.uk # Host rule for the Traefik routers
|
# (put Cloudflare / your TLS proxy in front of it)
|
||||||
TRAEFIK_ENTRYPOINT=websecure-external # Traefik entrypoint name on the host
|
|
||||||
|
|||||||
+15
-29
@@ -1,8 +1,6 @@
|
|||||||
# Production stack (DOCKER_SPEC §2). Publishes no host ports — Traefik (the host's
|
# Production stack. The public-facing site is published on host port 1337 (put your
|
||||||
# existing reverse proxy) discovers these containers on the shared traefik_proxy
|
# TLS terminator / Cloudflare in front of it). nginx inside the site container proxies
|
||||||
# network via the labels below and routes cesnimda.co.uk to the site, and
|
# /api/contact to the relay over the internal network, so only one port is exposed.
|
||||||
# cesnimda.co.uk/api/contact to the relay. Entrypoint / cert-resolver names match
|
|
||||||
# the host Traefik convention; override via the .env values if they differ.
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
site:
|
site:
|
||||||
@@ -16,14 +14,11 @@ services:
|
|||||||
- /tmp
|
- /tmp
|
||||||
- /var/cache/nginx
|
- /var/cache/nginx
|
||||||
- /var/run
|
- /var/run
|
||||||
networks: [proxy]
|
ports:
|
||||||
labels:
|
- '${SITE_PORT:-1337}:1337'
|
||||||
- traefik.enable=true
|
depends_on:
|
||||||
- traefik.docker.network=${PROXY_NETWORK:-traefik_proxy}
|
- relay
|
||||||
- traefik.http.routers.resumesite.rule=Host(`${SITE_HOST:-cesnimda.co.uk}`)
|
networks: [web]
|
||||||
- traefik.http.routers.resumesite.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure-external}
|
|
||||||
- traefik.http.routers.resumesite.tls=true
|
|
||||||
- traefik.http.services.resumesite.loadbalancer.server.port=8080
|
|
||||||
logging:
|
logging:
|
||||||
driver: json-file
|
driver: json-file
|
||||||
options: { max-size: '10m', max-file: '3' }
|
options: { max-size: '10m', max-file: '3' }
|
||||||
@@ -36,29 +31,20 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
read_only: true
|
read_only: true
|
||||||
environment:
|
environment:
|
||||||
- Smtp__Host=${SMTP_HOST}
|
- Smtp__Host=${SMTP_HOST:-}
|
||||||
- Smtp__Port=${SMTP_PORT:-587}
|
- Smtp__Port=${SMTP_PORT:-587}
|
||||||
- Smtp__User=${SMTP_USER}
|
- Smtp__User=${SMTP_USER:-}
|
||||||
- Smtp__Password=${SMTP_PASSWORD}
|
- Smtp__Password=${SMTP_PASSWORD:-}
|
||||||
- Relay__FromAddress=${RELAY_FROM:-}
|
- Relay__FromAddress=${RELAY_FROM:-}
|
||||||
- Relay__ToAddress=${RELAY_TO}
|
- Relay__ToAddress=${RELAY_TO:-connor.babbington@cesnimda.co.uk}
|
||||||
- Relay__AllowedOrigin=${RELAY_ALLOWED_ORIGIN:-https://cesnimda.co.uk}
|
- Relay__AllowedOrigin=${RELAY_ALLOWED_ORIGIN:-https://cesnimda.co.uk}
|
||||||
- Relay__RateLimitPerWindow=${RELAY_RATE_LIMIT:-5}
|
- Relay__RateLimitPerWindow=${RELAY_RATE_LIMIT:-5}
|
||||||
- Relay__WindowSeconds=${RELAY_WINDOW_SECONDS:-600}
|
- Relay__WindowSeconds=${RELAY_WINDOW_SECONDS:-600}
|
||||||
networks: [proxy]
|
networks: [web]
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.docker.network=${PROXY_NETWORK:-traefik_proxy}
|
|
||||||
# More specific rule than the site router, so /api/contact wins.
|
|
||||||
- traefik.http.routers.resumerelay.rule=Host(`${SITE_HOST:-cesnimda.co.uk}`) && PathPrefix(`/api/contact`)
|
|
||||||
- traefik.http.routers.resumerelay.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure-external}
|
|
||||||
- traefik.http.routers.resumerelay.tls=true
|
|
||||||
- traefik.http.services.resumerelay.loadbalancer.server.port=8081
|
|
||||||
logging:
|
logging:
|
||||||
driver: json-file
|
driver: json-file
|
||||||
options: { max-size: '10m', max-file: '3' }
|
options: { max-size: '10m', max-file: '3' }
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
web:
|
||||||
external: true
|
driver: bridge
|
||||||
name: ${PROXY_NETWORK:-traefik_proxy}
|
|
||||||
|
|||||||
+1
-1
@@ -14,5 +14,5 @@ RUN pnpm build
|
|||||||
FROM nginxinc/nginx-unprivileged:1.27-alpine AS final
|
FROM nginxinc/nginx-unprivileged:1.27-alpine AS final
|
||||||
COPY --chown=nginx:nginx nginx.conf /etc/nginx/conf.d/default.conf
|
COPY --chown=nginx:nginx nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY --from=build --chown=nginx:nginx /app/dist /usr/share/nginx/html
|
COPY --from=build --chown=nginx:nginx /app/dist /usr/share/nginx/html
|
||||||
EXPOSE 8080
|
EXPOSE 1337
|
||||||
# runs as uid 101 (nginx) by default in this image
|
# runs as uid 101 (nginx) by default in this image
|
||||||
|
|||||||
+12
-5
@@ -3,7 +3,7 @@
|
|||||||
# trailing slashes, the preserved /Linkedin redirect, and legacy-WP 410s.
|
# trailing slashes, the preserved /Linkedin redirect, and legacy-WP 410s.
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 8080;
|
listen 1337;
|
||||||
server_name _;
|
server_name _;
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
@@ -32,6 +32,14 @@ server {
|
|||||||
return 301 https://www.linkedin.com/in/connor-babbington;
|
return 301 https://www.linkedin.com/in/connor-babbington;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Contact relay (same-origin; single public port) ---
|
||||||
|
location = /api/contact {
|
||||||
|
proxy_pass http://relay:8081;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
# --- Legacy WordPress URLs -> 410 Gone (crawler cleanup) ---
|
# --- Legacy WordPress URLs -> 410 Gone (crawler cleanup) ---
|
||||||
location ~* ^/(wp-admin|wp-login|wp-content|wp-includes|wp-json|xmlrpc\.php|feed|comments/feed) {
|
location ~* ^/(wp-admin|wp-login|wp-content|wp-includes|wp-json|xmlrpc\.php|feed|comments/feed) {
|
||||||
return 410;
|
return 410;
|
||||||
@@ -59,11 +67,10 @@ server {
|
|||||||
add_header Cache-Control "public, must-revalidate" always;
|
add_header Cache-Control "public, must-revalidate" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Canonical trailing slash for extensionless paths (SEO) ---
|
# --- HTML documents: canonical trailing slash + revalidate. Kept inside the
|
||||||
rewrite ^([^.]*[^/])$ $1/ permanent;
|
# catch-all so exact routes (e.g. /api/contact) are never slash-redirected. ---
|
||||||
|
|
||||||
# --- HTML documents: revalidate so deploys are instant (inherits headers above) ---
|
|
||||||
location / {
|
location / {
|
||||||
|
rewrite ^([^.]*[^/])$ $1/ permanent;
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user