Il contesto

Un sito che doveva fare due cose bene

Questo sito — quello che stai leggendo ora — è stato costruito con un obiettivo preciso: personal branding e lead generation, con una base tecnica abbastanza solida da reggere nel tempo senza bisogno di manutenzione continua.

Il vincolo principale era la semplicità operativa: deploy in un comando, contenuti gestibili senza toccare il codice, SEO tecnica corretta fin dall'inizio. Nessun CMS esterno, nessuna dipendenza da servizi terzi, nessun lock-in. Identità digitale e SEO locale da zero

Stack e architettura

Next.js 16 App Router + MDX

La scelta di Next.js 16 con App Router non è stata scontata — avrei potuto usare Astro o un sito statico puro. Ho scelto Next.js per tre motivi: rendering ibrido (SSG per i contenuti, SSR dove serve), ecosistema maturo, e familiarità con il tooling.

App Router
Layout annidati, generateStaticParams per blog e portfolio, generateMetadata per SEO dinamica per ogni pagina e ogni articolo
MDX
Contenuti scritti in Markdown con componenti React inline. Nessun CMS: un file .mdx per ogni articolo e ogni case study, versionato in Git
CSS modulare
Architettura CSS divisa in quattro file: common.css, site.css, blog.css, portfolio.css. CSS vanilla con custom properties come sistema principale, Tailwind usato solo per utility sparse
TypeScript
Tipi per i frontmatter MDX, per i metadata delle pagine, per i componenti. Nessun any, tutto tipizzato dall'inizio
Struttura file
32 file, struttura src/app/ con route groups, content/blog/ e content/portfolio/ per i contenuti MDX, public/ per assets statici
Fonts & assets
Font caricati via next/font con preload automatico. Immagini in .webp e .avif per performance. Logo in PNG con preload esplicito nel <head> per evitare layout shift durante il caricamento della navbar.
SEO tecnica

JSON-LD, canonical URL e Open Graph

La SEO tecnica è stata progettata dall'inizio, non aggiunta dopo. Ogni tipo di pagina ha il suo schema JSON-LD appropriato:

01

Homepage — WebSite

Schema WebSite con SearchAction e ItemList per blog e portfolio. Canonical senza trailing slash, Open Graph con immagine dedicata og-image.webp (1200×630).

02

Articoli blog — Article

Schema Article con headline, description, author, datePublished, image per l'immagine dell'articolo, e publisher con logo che punta al logo del sito. Keywords estratte dal frontmatter MDX.

03

Case study — CreativeWork

Schema CreativeWork con provider, author, keywords dallo stack array del frontmatter. Immagine del progetto nel campo image.

04

Listing pages — ItemList

Le pagine /blog e /portfolio espongono un ItemList con tutti gli articoli/progetti indicizzati, per massimizzare la presenza nei rich results.

Il generateMetadata di Next.js costruisce dinamicamente per ogni pagina: title, description, canonical, og:url, og:image, twitter:card. Nessun meta tag scritto a mano.

Un dettaglio importante: tutti gli URL usano https://ekulos.com senza www — la coerenza tra canonical, og:url e JSON-LD è necessaria per evitare segnali contrastanti a Google.

Deploy e infrastruttura

Docker + Watchtower + GitHub Actions

Il sito gira su un home server, senza CDN né proxy cloud. Il deploy è completamente automatizzato:

GitHub Actions
CI/CD su push a main: build dell'immagine Docker, push su ghcr.io, tag con SHA del commit per rollback preciso
Docker Compose
Container Next.js standalone con output: 'standalone' in next.config.ts per immagini minimali. Volume per assets statici
Watchtower
Polling automatico su ghcr.io: quando arriva una nuova immagine, Watchtower aggiorna il container in running senza downtime manuale
Justfile
Comandi operativi raccolti in un Justfile: just deploy, just logs, just release, just clean. Zero memoria richiesta per operazioni di routine

Il flusso completo da commit a produzione:

`` git push origin main → GitHub Actions build + push ghcr.io → Watchtower polling (intervallo configurabile) → Container aggiornato in produzione `

Nessun SSH manuale, nessuno script da ricordare.

Scelte progettuali

Cosa ho scelto di non fare

Alcune decisioni contano quanto quelle su cosa includere. CSS vanilla come sistema principale — architettura modulare con custom properties, Tailwind usato solo per utility sparse dove ha senso. Nessun framework CSS che guida le scelte architetturali: la struttura visiva è interamente in CSS modulare, più leggibile e più facile da debuggare. Nessun CMS — MDX in Git è sufficiente. Aggiungere un CMS headless avrebbe significato una dipendenza esterna, un costo mensile, e complessità senza beneficio reale. Nessun analytics di terze parti — nessun script di tracking, nessun cookie banner. Google Search Console per i dati SEO è tutto quello che serve. Static generation over server rendering — tutte le pagine sono pre-renderizzate a build time con generateStaticParams. Il server non fa lavoro a runtime, i Time to First Byte sono minimi.

"Il criterio principale era: se tra due anni devo aggiungere un articolo, quanto ci mette? La risposta deve essere: creo un file .mdx`, faccio push, deploy automatico in cinque minuti."
EK
Ekulos
Note di progetto