feat: homelab case study from live infra; Traefik-based deploy
- rewrote homelab (content + topology diagram + stack) from a live inspection of the host: Ubuntu 24.04, ~30 Docker services behind Traefik (Cloudflare-fronted, TLS, HTTP/3), Authentik SSO forward-auth, CrowdSec, Pi-hole, self-hosted Gitea + CI runner, socket-proxy, per-app network isolation; WordPress framed as being decommissioned (not future arch) - deploy: docker-compose now uses Traefik labels + traefik_proxy network (was assumed nginx edge); .env.example adds SITE_HOST/TRAEFIK_ENTRYPOINT; colophon + ARCHITECTURE/DOCKER/ DEPLOYMENT specs corrected nginx-edge -> Traefik (site container still serves via nginx) - PROJECT_STATUS: pre-launch checklist updated; infra section added Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+19
-11
@@ -59,19 +59,27 @@
|
||||
6. **Theme-init is an external script** (`/theme-init.js`) so the CSP can use `script-src
|
||||
'self'` with no inline hash.
|
||||
|
||||
## Pre-launch checklist (carried from Phase 1/2 — owner action)
|
||||
## Pre-launch checklist (owner action)
|
||||
|
||||
- [ ] **Replace the `/Linkedin` redirect target** in `site/nginx.conf` with the real LinkedIn
|
||||
profile URL before cutover (currently a placeholder slug).
|
||||
- [ ] Screenshot capture pass on **seeded demo data** for JobTrack + InboxIntel; drop real
|
||||
captures into the `placeholder:` media refs (`src/data/projects/*`).
|
||||
- [ ] Drop the real **outdoor headshot** into the portrait ref (replaces `placeholder:portrait`).
|
||||
- [ ] **ATS-safe CV rework** (EN/NO) — replace the PDFs at `site/public/cv/` (same URLs).
|
||||
- [ ] **Native-Norwegian review** of all NO content (launch gate).
|
||||
- [x] ~~`/Linkedin` redirect target~~ — set to `https://www.linkedin.com/in/connor-babbington`.
|
||||
- [x] ~~JobTrack seed-data screenshots~~ — real captures integrated (`/assets/projects/jobtrack/`).
|
||||
- [x] ~~Real headshot~~ — integrated (`/assets/portrait.webp`, hero + about).
|
||||
- [x] ~~ATS-safe CV rework (EN/NO)~~ — done; generator in `tools/cv/`.
|
||||
- [ ] **InboxIntel screenshots** — still placeholders (no captures provided; it is "in development").
|
||||
- [ ] **Native-Norwegian review** of all NO content, incl. the NO CV (launch gate).
|
||||
- [ ] **Legacy WordPress URL inventory** → confirm the 301/410 map in `nginx.conf`.
|
||||
- [ ] Configure CI secrets (`REGISTRY_USER`, `REGISTRY_TOKEN`) + the relay `.env` on the server.
|
||||
- [ ] Confirm the Traefik entrypoint / cert-resolver names in `deploy/.env.example` match the host.
|
||||
|
||||
## Open questions / blocked-on-user
|
||||
## Infrastructure (verified live, July 2026)
|
||||
|
||||
- Are the JobTrack / InboxIntel repos public? If so, add `repo` links in `src/data/projects/*`
|
||||
(currently omitted to avoid dead links).
|
||||
Deploy target is the `mediaserver` host (Ubuntu 24.04): ~30 Docker services behind **Traefik**
|
||||
(Cloudflare-fronted, TLS, HTTP/3), with **Authentik** SSO, **CrowdSec**, **Pi-hole**, and
|
||||
self-hosted **Gitea + Actions runner** (the CI/CD target for this site). The homelab case study
|
||||
and `deploy/` now reflect this (Traefik labels, `traefik_proxy` network). WordPress still runs
|
||||
but is being decommissioned as this site replaces it — not documented as future architecture.
|
||||
|
||||
## Resolved
|
||||
|
||||
- JobTrack / InboxIntel repos are **private** → repo links intentionally omitted; case studies
|
||||
describe the architecture without linking code.
|
||||
|
||||
+4
-2
@@ -14,5 +14,7 @@ RELAY_WINDOW_SECONDS=600
|
||||
# --- Site build ---
|
||||
PUBLIC_SITE_URL=https://cesnimda.co.uk
|
||||
|
||||
# --- Infra ---
|
||||
PROXY_NETWORK=web # name of the existing external reverse-proxy docker network
|
||||
# --- Infra (Traefik host reverse proxy) ---
|
||||
PROXY_NETWORK=traefik_proxy # existing external Traefik docker network
|
||||
SITE_HOST=cesnimda.co.uk # Host rule for the Traefik routers
|
||||
TRAEFIK_ENTRYPOINT=websecure-external # Traefik entrypoint name on the host
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Production stack (DOCKER_SPEC §2). Publishes no host ports — the existing host
|
||||
# reverse proxy routes cesnimda.co.uk -> site:8080 and /api/contact -> relay:8081
|
||||
# over the shared external proxy network.
|
||||
# Production stack (DOCKER_SPEC §2). Publishes no host ports — Traefik (the host's
|
||||
# existing reverse proxy) discovers these containers on the shared traefik_proxy
|
||||
# network via the labels below and routes cesnimda.co.uk to the site, and
|
||||
# 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:
|
||||
site:
|
||||
@@ -14,7 +16,14 @@ services:
|
||||
- /tmp
|
||||
- /var/cache/nginx
|
||||
- /var/run
|
||||
networks: [web]
|
||||
networks: [proxy]
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=${PROXY_NETWORK:-traefik_proxy}
|
||||
- traefik.http.routers.resumesite.rule=Host(`${SITE_HOST:-cesnimda.co.uk}`)
|
||||
- traefik.http.routers.resumesite.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure-external}
|
||||
- traefik.http.routers.resumesite.tls=true
|
||||
- traefik.http.services.resumesite.loadbalancer.server.port=8080
|
||||
logging:
|
||||
driver: json-file
|
||||
options: { max-size: '10m', max-file: '3' }
|
||||
@@ -36,12 +45,20 @@ services:
|
||||
- Relay__AllowedOrigin=${RELAY_ALLOWED_ORIGIN:-https://cesnimda.co.uk}
|
||||
- Relay__RateLimitPerWindow=${RELAY_RATE_LIMIT:-5}
|
||||
- Relay__WindowSeconds=${RELAY_WINDOW_SECONDS:-600}
|
||||
networks: [web]
|
||||
networks: [proxy]
|
||||
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:
|
||||
driver: json-file
|
||||
options: { max-size: '10m', max-file: '3' }
|
||||
|
||||
networks:
|
||||
web:
|
||||
proxy:
|
||||
external: true
|
||||
name: ${PROXY_NETWORK:-web}
|
||||
name: ${PROXY_NETWORK:-traefik_proxy}
|
||||
|
||||
@@ -7,7 +7,7 @@ System architecture for the bilingual portfolio. Companion detail specs: ROUTING
|
||||
|
||||
```
|
||||
┌──────────────────────────── Connor's server ───────────────────────────┐
|
||||
Browser ── HTTPS ──► │ nginx (existing reverse proxy, TLS) │
|
||||
Browser ── HTTPS ──► │ Traefik (existing host reverse proxy, TLS; Cloudflare-fronted) │
|
||||
│ ├── cesnimda.co.uk/ → [site] nginx container (static) │
|
||||
│ ├── cesnimda.co.uk/api/contact → [contact-relay] .NET 9 container │
|
||||
│ └── git.cesnimda.uk → Gitea (existing) │
|
||||
|
||||
@@ -33,7 +33,7 @@ push / PR to main
|
||||
|
||||
1. Pre-launch: staging fully verified (both locales, CV downloads, form → real mailbox, redirects incl. `/Linkedin`, 410 map, OG previews via LinkedIn/Slack debuggers, Search Console fetch).
|
||||
2. URL inventory + redirect/410 map finalised (SEO_SPEC §6).
|
||||
3. Cutover = repoint host nginx server-block from WP to the `site` container (single reload; WP left running dark for 2 weeks as instant rollback).
|
||||
3. Cutover = move the `cesnimda.co.uk` Traefik Host router from the WordPress container to the `site` container (a label change + `compose up -d`; WP left running dark for 2 weeks as instant rollback).
|
||||
4. Post-launch checklist: submit sitemaps, watch Search Console coverage + relay logs for form spam patterns, then decommission WP + its PHP/MySQL surface (attack-surface win worth noting on the colophon).
|
||||
|
||||
## 4. Operational guardrails
|
||||
|
||||
@@ -31,7 +31,7 @@ deploy/
|
||||
.env.example # documented variables, no real values
|
||||
```
|
||||
|
||||
- Prod compose joins the server's existing reverse-proxy network; the host nginx (existing, TLS-terminating) routes `cesnimda.co.uk` → `site:8080` and `cesnimda.co.uk/api/contact` → `contact-relay:8081`. The stack publishes **no host ports** itself.
|
||||
- Prod compose joins the server's existing Traefik network (`traefik_proxy`); the host Traefik (existing, TLS-terminating, Cloudflare-fronted) discovers the containers via labels and routes `cesnimda.co.uk` → `site:8080` and `cesnimda.co.uk/api/contact` → `relay:8081` (the relay's `PathPrefix` rule outranks the site's `Host` rule). The stack publishes **no host ports** itself.
|
||||
- `restart: unless-stopped`, log rotation via compose logging options, images referenced by registry tag + digest.
|
||||
- Optional third service slot (`analytics`, self-hosted Umami/Plausible) reserved in the compose file as a commented profile — decision deferred.
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ const copy = {
|
||||
],
|
||||
[
|
||||
'Self-hosted',
|
||||
'Built and deployed from my own Gitea instance to Docker behind nginx. The contact form is a small stateless .NET service — even that is part of my own stack.',
|
||||
'Built and deployed from my own Gitea instance to Docker behind Traefik. The contact form is a small stateless .NET service — even that is part of my own stack.',
|
||||
],
|
||||
[
|
||||
'No tracking',
|
||||
@@ -59,7 +59,7 @@ const copy = {
|
||||
],
|
||||
[
|
||||
'Egendriftet',
|
||||
'Bygget og rullet ut fra min egen Gitea-instans til Docker bak nginx. Kontaktskjemaet er en liten tilstandsløs .NET-tjeneste — også den er en del av min egen stack.',
|
||||
'Bygget og rullet ut fra min egen Gitea-instans til Docker bak Traefik. Kontaktskjemaet er en liten tilstandsløs .NET-tjeneste — også den er en del av min egen stack.',
|
||||
],
|
||||
[
|
||||
'Ingen sporing',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { Project } from '@lib/schema';
|
||||
|
||||
/* Homelab — capability page (lighter template): no TL;DR/decisions/gallery rendered. */
|
||||
/*
|
||||
Homelab — capability page (lighter template): no TL;DR/decisions/gallery rendered.
|
||||
Content reflects the real setup inspected on the host (Ubuntu 24.04, ~30 Docker
|
||||
services behind Traefik with Authentik SSO, CrowdSec, Pi-hole, Gitea CI).
|
||||
*/
|
||||
export const homelab: Project = {
|
||||
id: 'homelab',
|
||||
name: 'Self-hosted infrastructure lab',
|
||||
@@ -10,88 +14,100 @@ export const homelab: Project = {
|
||||
stack: [
|
||||
{ name: 'Ubuntu' },
|
||||
{ name: 'Docker' },
|
||||
{ name: 'nginx' },
|
||||
{ name: 'Gitea' },
|
||||
{ name: 'Monitoring' },
|
||||
{ name: 'Backups' },
|
||||
{ name: 'Traefik' },
|
||||
{ name: 'Authentik' },
|
||||
{ name: 'CrowdSec' },
|
||||
{ name: 'Gitea CI' },
|
||||
],
|
||||
links: [],
|
||||
diagram: {
|
||||
viewBox: '0 0 1120 300',
|
||||
viewBox: '0 0 1120 310',
|
||||
title: { en: 'Homelab topology', no: 'Hjemmelab-topologi' },
|
||||
desc: {
|
||||
en: 'Inbound traffic reaches an nginx reverse proxy terminating TLS, which routes to self-hosted services: Gitea, application containers and a monitoring stack.',
|
||||
no: 'Innkommende trafikk når en nginx reverse proxy som terminerer TLS, og rutes videre til egendriftede tjenester: Gitea, applikasjonscontainere og en overvåkingsstack.',
|
||||
en: 'Traffic arrives through Cloudflare to a Traefik reverse proxy that terminates TLS and applies Authentik single sign-on, then routes to self-hosted services: Gitea and its CI runner, application containers, and a media stack.',
|
||||
no: 'Trafikk kommer via Cloudflare til en Traefik reverse proxy som terminerer TLS og bruker Authentik single sign-on, og rutes videre til egendriftede tjenester: Gitea med CI-runner, applikasjonscontainere og en mediastack.',
|
||||
},
|
||||
nodes: [
|
||||
{
|
||||
id: 'internet',
|
||||
id: 'cloudflare',
|
||||
x: 40,
|
||||
y: 116,
|
||||
w: 160,
|
||||
h: 72,
|
||||
y: 124,
|
||||
w: 170,
|
||||
h: 68,
|
||||
kind: 'external',
|
||||
label: 'Internet',
|
||||
sub: { en: 'HTTPS', no: 'HTTPS' },
|
||||
label: 'Cloudflare',
|
||||
sub: { en: 'DNS · edge · WAF', no: 'DNS · edge · WAF' },
|
||||
},
|
||||
{
|
||||
id: 'proxy',
|
||||
x: 280,
|
||||
y: 116,
|
||||
w: 190,
|
||||
h: 72,
|
||||
id: 'traefik',
|
||||
x: 290,
|
||||
y: 124,
|
||||
w: 180,
|
||||
h: 68,
|
||||
kind: 'primary',
|
||||
label: 'nginx',
|
||||
label: 'Traefik',
|
||||
sub: { en: 'reverse proxy · TLS', no: 'reverse proxy · TLS' },
|
||||
},
|
||||
{
|
||||
id: 'authentik',
|
||||
x: 290,
|
||||
y: 232,
|
||||
w: 180,
|
||||
h: 56,
|
||||
kind: 'internal',
|
||||
label: 'Authentik',
|
||||
sub: { en: 'SSO · forward-auth', no: 'SSO · forward-auth' },
|
||||
},
|
||||
{
|
||||
id: 'gitea',
|
||||
x: 560,
|
||||
y: 40,
|
||||
w: 190,
|
||||
y: 44,
|
||||
w: 210,
|
||||
h: 60,
|
||||
kind: 'internal',
|
||||
label: 'Gitea',
|
||||
sub: { en: 'git.cesnimda.uk', no: 'git.cesnimda.uk' },
|
||||
label: 'Gitea + runner',
|
||||
sub: { en: 'git.cesnimda.uk · CI/CD', no: 'git.cesnimda.uk · CI/CD' },
|
||||
},
|
||||
{
|
||||
id: 'apps',
|
||||
x: 560,
|
||||
y: 120,
|
||||
w: 190,
|
||||
y: 128,
|
||||
w: 210,
|
||||
h: 60,
|
||||
kind: 'internal',
|
||||
label: 'App services',
|
||||
sub: { en: 'Docker containers', no: 'Docker-containere' },
|
||||
label: 'App containers',
|
||||
sub: { en: 'JobTrack · this site', no: 'JobTrack · denne siden' },
|
||||
},
|
||||
{
|
||||
id: 'monitor',
|
||||
id: 'media',
|
||||
x: 560,
|
||||
y: 200,
|
||||
w: 190,
|
||||
y: 212,
|
||||
w: 210,
|
||||
h: 60,
|
||||
kind: 'internal',
|
||||
label: 'Monitoring',
|
||||
sub: { en: 'uptime · logs', no: 'oppetid · logger' },
|
||||
label: 'Media stack',
|
||||
sub: { en: 'Jellyfin · *arr', no: 'Jellyfin · *arr' },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{ d: 'M200 152 H280', kind: 'flow' },
|
||||
{ d: 'M470 140 L560 70', kind: 'flow' },
|
||||
{ d: 'M470 152 H560', kind: 'flow' },
|
||||
{ d: 'M470 164 L560 230', kind: 'flow' },
|
||||
{ d: 'M210 158 H290', kind: 'flow' },
|
||||
{ d: 'M380 192 V232', kind: 'flow', label: 'auth', labelX: 400, labelY: 218 },
|
||||
{ d: 'M470 150 L560 74', kind: 'flow' },
|
||||
{ d: 'M470 158 H560', kind: 'flow' },
|
||||
{ d: 'M470 166 L560 242', kind: 'flow' },
|
||||
],
|
||||
},
|
||||
media: [],
|
||||
content: {
|
||||
en: {
|
||||
valueProp: 'Where I run, break and fix real infrastructure — not just deploy it.',
|
||||
valueProp:
|
||||
'Where I run, break and fix real infrastructure — around thirty containerised services behind a single reverse proxy.',
|
||||
cardTeaser:
|
||||
'Ubuntu · Docker services · reverse proxy · auth · monitoring · Gitea (this site’s own git remote).',
|
||||
'Ubuntu + Docker · Traefik · Authentik SSO · CrowdSec · Gitea CI — ~30 self-hosted services, and the deploy target for this site.',
|
||||
tldr: {
|
||||
what: 'A self-hosted home lab running production-style services.',
|
||||
why: 'To keep operations skills hands-on.',
|
||||
stack: 'Ubuntu · Docker · nginx · Gitea · monitoring.',
|
||||
what: 'A self-hosted home server running ~30 production-style services.',
|
||||
why: 'To keep operations and security skills hands-on.',
|
||||
stack: 'Ubuntu · Docker · Traefik · Authentik · CrowdSec · Gitea.',
|
||||
role: 'Operator.',
|
||||
},
|
||||
sections: [
|
||||
@@ -102,7 +118,7 @@ export const homelab: Project = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'p',
|
||||
text: 'A home lab running Ubuntu and Docker-based services behind an nginx reverse proxy, with authentication, monitoring and backups. It’s where I get ongoing, hands-on practice with deployment, troubleshooting and real-world operations — including the self-hosted Gitea instance that this very site is built and deployed from.',
|
||||
text: 'An Ubuntu 24.04 server running around thirty Docker services behind a Traefik reverse proxy, with single sign-on, an intrusion-prevention layer, DNS filtering and a full CI/CD setup. It’s where I get ongoing, hands-on practice with deployment, security and real-world operations — including the self-hosted Gitea instance and runner that build and deploy this very portfolio.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -114,10 +130,11 @@ export const homelab: Project = {
|
||||
{
|
||||
type: 'ul',
|
||||
items: [
|
||||
'nginx reverse proxy terminating TLS in front of every service.',
|
||||
'Docker Compose stacks for applications, each isolated on its own network.',
|
||||
'Authentication in front of internal tools; monitoring for uptime and logs.',
|
||||
'Self-hosted Gitea with CI runners — the deployment target for this portfolio.',
|
||||
'Traefik terminates TLS and routes every service, fronted by Cloudflare, with separate internal and external entrypoints and HTTP/3.',
|
||||
'Authentik provides single sign-on as a Traefik forward-auth middleware in front of internal tools; CrowdSec adds intrusion prevention and Pi-hole handles DNS filtering.',
|
||||
'Gitea and a Gitea Actions runner give me self-hosted git and CI/CD — the build-and-deploy target for this site.',
|
||||
'The Docker socket is exposed to Traefik only through a hardened socket-proxy, and each application runs on its own isolated Docker network.',
|
||||
'Portainer and Dozzle handle container management and logs; alongside these run application, media and utility stacks.',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -129,7 +146,7 @@ export const homelab: Project = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'p',
|
||||
text: 'Tightening backup verification and adding more automated health checks and alerting across the stack.',
|
||||
text: 'Retiring the legacy WordPress site now that this portfolio replaces it, then tightening backup verification and expanding automated health checks and alerting across the stack.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -137,13 +154,13 @@ export const homelab: Project = {
|
||||
},
|
||||
no: {
|
||||
valueProp:
|
||||
'Der jeg drifter, ødelegger og fikser ekte infrastruktur — ikke bare ruller den ut.',
|
||||
'Der jeg drifter, ødelegger og fikser ekte infrastruktur — rundt tretti containeriserte tjenester bak én reverse proxy.',
|
||||
cardTeaser:
|
||||
'Ubuntu · Docker-tjenester · reverse proxy · auth · overvåking · Gitea (git-remoten til denne siden).',
|
||||
'Ubuntu + Docker · Traefik · Authentik SSO · CrowdSec · Gitea CI — ~30 egendriftede tjenester, og utrullingsmålet for denne siden.',
|
||||
tldr: {
|
||||
what: 'En egendriftet hjemmelab som kjører produksjonslignende tjenester.',
|
||||
why: 'For å holde driftsferdighetene praktiske.',
|
||||
stack: 'Ubuntu · Docker · nginx · Gitea · overvåking.',
|
||||
what: 'En egendriftet hjemmeserver med ~30 produksjonslignende tjenester.',
|
||||
why: 'For å holde drifts- og sikkerhetsferdighetene praktiske.',
|
||||
stack: 'Ubuntu · Docker · Traefik · Authentik · CrowdSec · Gitea.',
|
||||
role: 'Operatør.',
|
||||
},
|
||||
sections: [
|
||||
@@ -154,7 +171,7 @@ export const homelab: Project = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'p',
|
||||
text: 'En hjemmelab som kjører Ubuntu og Docker-baserte tjenester bak en nginx reverse proxy, med autentisering, overvåking og sikkerhetskopier. Det er her jeg får løpende, praktisk øving på utrulling, feilsøking og ekte drift — inkludert den egendriftede Gitea-instansen som denne siden bygges og rulles ut fra.',
|
||||
text: 'En Ubuntu 24.04-server som kjører rundt tretti Docker-tjenester bak en Traefik reverse proxy, med single sign-on, et lag for inntrengingsvern, DNS-filtrering og full CI/CD. Det er her jeg får løpende, praktisk øving på utrulling, sikkerhet og ekte drift — inkludert den egendriftede Gitea-instansen og runneren som bygger og ruller ut denne porteføljen.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -166,10 +183,11 @@ export const homelab: Project = {
|
||||
{
|
||||
type: 'ul',
|
||||
items: [
|
||||
'nginx reverse proxy som terminerer TLS foran hver tjeneste.',
|
||||
'Docker Compose-stacker for applikasjoner, hver isolert på eget nettverk.',
|
||||
'Autentisering foran interne verktøy; overvåking av oppetid og logger.',
|
||||
'Egendriftet Gitea med CI-runnere — utrullingsmålet for denne porteføljen.',
|
||||
'Traefik terminerer TLS og ruter alle tjenester, med Cloudflare foran, egne interne og eksterne entrypoints og HTTP/3.',
|
||||
'Authentik gir single sign-on som en Traefik forward-auth-middleware foran interne verktøy; CrowdSec legger til inntrengingsvern og Pi-hole håndterer DNS-filtrering.',
|
||||
'Gitea og en Gitea Actions-runner gir egendriftet git og CI/CD — bygge- og utrullingsmålet for denne siden.',
|
||||
'Docker-socketen eksponeres kun til Traefik gjennom en herdet socket-proxy, og hver applikasjon kjører på sitt eget isolerte Docker-nettverk.',
|
||||
'Portainer og Dozzle håndterer containere og logger; ved siden av kjører applikasjons-, media- og verktøystacker.',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -181,7 +199,7 @@ export const homelab: Project = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'p',
|
||||
text: 'Strammere verifisering av sikkerhetskopier og flere automatiske helsesjekker og varsling på tvers av stacken.',
|
||||
text: 'Legge ned den gamle WordPress-siden nå som denne porteføljen erstatter den, og deretter stramme inn verifisering av sikkerhetskopier og utvide automatiske helsesjekker og varsling på tvers av stacken.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user