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.

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.
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:
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).
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.
Case study — CreativeWork
Schema `CreativeWork` con `provider`, `author`, `keywords` dallo stack array del frontmatter. Immagine del progetto nel campo `image`.
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.
Docker + Watchtower + GitHub Actions
Il sito gira su un home server, senza CDN né proxy cloud. Il deploy è completamente automatizzato:
Il flusso completo da commit a produzione:
git push origin main
→ GitHub Actions build + push ghcr.io
→ Watchtower polling (intervallo configurabile)
→ Container aggiornato in produzioneNessun SSH manuale, nessuno script da ricordare.
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."