Elido
15 min di letturaIngegneria
Pilastro

Self-hosting di Elido su k3s - un playbook completo

Una guida passo dopo passo per deployare lo stack completo di Elido su un cluster k3s: bootstrap Helm, 14 servizi, il data plane come StatefulSet, Caddy on-demand TLS, backup e strategia di aggiornamento.

Marius Voß
DevRel · edge infra
Architecture diagram of Elido on k3s: edge-redirect and api-core Deployments in front, StatefulSets for Postgres, Redis, ClickHouse, Redpanda, MinIO, and Meilisearch behind, Caddy Ingress handling on-demand TLS at the top

La versione managed di Elido gira su un'infrastruttura in regione UE con una configurazione privacy-by-default. Per la maggior parte degli utenti, questo è sufficiente. Per alcuni, non lo è.

Se il tuo team di sicurezza richiede che i dati di destinazione dei link abbreviati e gli eventi di clic non lascino mai un datacenter specifico, se la tua policy di audit esige il controllo completo del server di database, o se stai integrando Elido in una piattaforma interna che deve funzionare air-gapped, il percorso self-hosted esiste esattamente per questo scopo.

Questa guida illustra come deployare Elido su k3s, una distribuzione Kubernetes minimale di livello produzione che gira comodamente su una singola VM o su un piccolo cluster HA. Alla fine avrai ogni servizio in esecuzione, TLS predisposto, backup configurati e un percorso di aggiornamento ripetibile. La guida presuppone che tu voglia un sistema funzionante, non un tutorial sui concetti Kubernetes - le spiegazioni sono mantenute brevi e i passaggi operativi sono espliciti.

Perché fare il self-hosting#

Prima di entrare nel playbook, vale la pena essere precisi sui trade-off. Il self-hosting non è automaticamente meglio - sposta il rischio operativo da un fornitore al tuo team.

Residenza dei dati e compliance. Se il tuo framework di conformità (ambito ISO 27001, policy interna di classificazione dei dati o clausola contrattuale di residenza dei dati) richiede che i metadati dei link e gli eventi di analytics rimangano su un'infrastruttura che controlli direttamente, il SaaS managed non può soddisfare questo requisito indipendentemente da dove si trova fisicamente. Un deployment self-hosted su una VM in regione UE che possiedi sì. Vedi la panoramica della compliance per i dettagli su come l'architettura di Elido si mappa sui framework più comuni.

Prevedibilità dei costi su larga scala. I piani managed hanno un prezzo basato su link attivi e volume di clic. Oltre una certa soglia - tipicamente da qualche parte sopra qualche milione di clic al mese - il costo per evento sui piani managed supera il costo dell'infrastruttura per eseguire il workload StatefulSet equivalente da soli. Il punto di pareggio dipende dalla forma del tuo traffico, ma esiste.

Controllo dell'audit. Alcune organizzazioni richiedono l'accesso alle tabelle Postgres grezze e ai dati degli eventi di clic ClickHouse per la raccolta delle prove, il legal hold o l'integrazione SIEM. L'API di Elido espone endpoint del log di audit ed export delle prove, ma l'accesso diretto al database è disponibile solo nei deployment self-hosted.

Cosa stai cedendo in cambio. k3s richiede che qualcuno si occupi degli aggiornamenti, della salute dei nodi, della verifica dei backup e della risposta agli incidenti. Se il tuo team non ha esperienza operativa con Kubernetes, la versione managed in una posizione di hosting conforme è quasi certamente la risposta giusta. Vedi l'ultima sezione di questo post per un trattamento più diretto di quando il self-hosting è la scelta sbagliata.

Prerequisiti#

  • Un cluster k3s. Un singolo nodo con 4 vCPU e 8 GB di RAM gestisce workload leggeri. Per HA, tre nodi control-plane più due o più nodi worker è la topologia minima raccomandata. etcd integrato di k3s copre l'HA del control-plane; l'HA del data plane è gestita da Patroni (Postgres) all'interno del chart Helm.
  • kubectl configurato con un context che punta al tuo cluster.
  • Helm 3.14 o versioni successive.
  • Un dominio che controlli con la possibilità di creare record DNS. L'IP ingress k3s deve essere raggiungibile sulle porte 80 e 443 da Internet affinché le sfide ACME di Let's Encrypt abbiano successo.
  • Se guidato dalla compliance, una VM in regione UE. Hetzner (Falkenstein, Helsinki, Norimberga) e OVH (Gravelines, Roubaix) sono i due provider che usa la propria infrastruttura edge di Elido.

Su un nuovo Hetzner CX32 (4 vCPU / 8 GB) con Ubuntu 24.04, k3s si installa in circa 30 secondi:

curl -sfL https://get.k3s.io | sh -
# Copia il kubeconfig sul tuo computer locale:
scp root@<your-vm>:/etc/rancher/k3s/k3s.yaml ~/.kube/elido-self-host.yaml
export KUBECONFIG=~/.kube/elido-self-host.yaml
kubectl get nodes  # dovrebbe mostrare Ready

Clona il repository Elido per ottenere il chart Helm (il chart si trova in deploy/helm/elido/ nel repo - non esiste un repository Helm pubblico separato):

git clone https://github.com/elidoapp/elido.git
cd elido

Quick start#

Il preset self-host (deploy/helm/elido/values-selfhost.yaml) è il punto di partenza consigliato per un deployment k3s su singolo nodo. Prima esegui il bootstrap dei segreti, poi installa:

# 1. Crea tutti i Kubernetes Secret necessari in un colpo solo (idempotente)
./scripts/bootstrap-secrets.sh elido

# 2. Installa usando il preset self-host
helm -n elido upgrade --install elido ./deploy/helm/elido \
  -f ./deploy/helm/elido/values-selfhost.yaml \
  --set ingress.hosts.redirect[0]=r.example.com \
  --set ingress.hosts.api=api.example.com \
  --set ingress.hosts.dashboard=app.example.com \
  --set image.tag=$(git rev-parse --short HEAD) \
  --create-namespace \
  --wait --timeout 10m

Per override personalizzati (conteggi di replica per la produzione, servizi esterni managed, ecc.), copia values-selfhost.yaml in un my-values.yaml locale, modificalo e passalo con -f. Le principali sezioni di valori di primo livello sono ingress.hosts.* (i tuoi hostname), image.registry/image.tag e i blocchi resources per servizio. Non usare global.domain - il chart usa ingress.hosts.* per la configurazione del dominio.

Il flag --wait si blocca finché tutti i Deployment e gli StatefulSet non raggiungono il loro stato pronto. Su un nodo nuovo senza immagini in cache, aspettati 5–8 minuti su un link da 1 Gbps.

Verifica che tutto sia partito:

kubectl -n elido get pods
kubectl -n elido get svc

Consulta la referenza dei valori del chart con helm show values ./deploy/helm/elido per la superficie completa dei parametri. Il file deploy/helm/elido/README.md nel repository è la fonte autorevole per i nomi specifici dei campi e l'utilizzo dello script di bootstrap.

Flusso di deploy Helm per Elido: bootstrap-secrets crea i Kubernetes Secret, helm upgrade install applica il chart con i values self-host, i manifest vengono applicati all'API server k3s, il cluster riconcilia 14 Deployment e 6 StatefulSet mentre ogni pod esegue migrate up all'avvio, e helm wait si blocca finche ogni pod non e Ready

L'architettura che stai deployando#

L'architettura di Elido è divisa per budget di latenza. Comprendere questa divisione ti aiuta a prendere decisioni informate sull'allocazione delle risorse.

Topologia del cluster k3s per Elido: un Caddy ingress in cima, il Deployment hot-path edge-redirect e i Deployment API warm-path sotto di esso, i Deployment worker cold-path a lato, e il data plane StatefulSet (Postgres, Redis, ClickHouse, Redpanda, MinIO, Meilisearch) in basso

Percorso hot: edge-redirect#

edge-redirect è l'unico servizio sul percorso sincrono di una richiesta di redirect. È scritto in Go con fasthttp e ha un budget di latenza rigido: p50 5ms, p95 15ms su un cache hit. Il servizio mantiene una cache a due livelli: un LRU in-process (L1) supportato da Redis Cluster (L2). Su un cache miss, ricade su una chiamata gRPC verso api-core. Gli eventi di clic vengono emessi fire-and-forget in Redpanda - la risposta di redirect non viene mai trattenuta in attesa che la scrittura dell'evento si completi.

Il chart Helm deploya edge-redirect come Deployment con un HorizontalPodAutoscaler. In un setup self-hosted single-region, due repliche è un punto di partenza ragionevole. La cache Redis L2 è condivisa tra le repliche, quindi il riscaldamento della cache è rapido dopo un rolling update.

Percorso warm: superficie API#

Cinque servizi gestiscono il lavoro sincrono API e di logica di business:

  • api-core - Go + chi, REST e gRPC. Source of truth per link, workspace, membership, domini personalizzati, eventi di audit.
  • api-bff - BFF layer Node/Hono per la dashboard web e i client mobile. Aggrega api-core e analytics-api.
  • analytics-api - Go, query ClickHouse. Serve le dashboard di analytics.
  • billing - Go + chi + sqlc. Motore EU VAT, integrazione pagamenti LiqPay, generazione fatture.
  • search - Go, proxya le query di ricerca dei link verso Meilisearch.

L'auth è gestita da Ory Kratos (identità, sessioni, verifica email) e Ory Hydra (token OAuth2/OIDC per le integrazioni di terze parti e l'estensione browser). Entrambi vengono deployati come Deployment nel chart.

Percorso cold: worker asincroni#

Sei servizi consumano eventi dai topic Redpanda e non hanno budget di tempo di risposta:

  • click-ingester - consuma gli eventi di clic, scrive su ClickHouse.
  • webhook-dispatcher - distribuisce payload webhook firmati agli endpoint dei clienti.
  • notification - notifiche email e in-app (eventi account, alert sui link).
  • url-scanner - esegue la scansione degli URL di destinazione contro Google Safe Browsing, PhishTank, SURBL.
  • metadata-fetcher - recupera i metadati Open Graph per le anteprime dei link.
  • domain-manager - verifica DNS e provisioning TLS on-demand Caddy per i domini personalizzati.

Data plane: StatefulSet#

Sei sistemi stateful supportano i servizi sopra:

SistemaRuoloRisorsa chart
Postgres (Patroni)Source-of-truth per link, utenti, billingStatefulSet, 3 repliche in modalità HA
Redis ClusterCache del percorso hot per i linkStatefulSet
ClickHouseArchiviazione eventi di clic e query di analyticsStatefulSet
RedpandaEvent bus tra i serviziStatefulSet
MinIOAsset caricati dagli utenti (immagini QR, export)StatefulSet
MeilisearchRicerca link in-appStatefulSet

In un deployment single-node, ogni StatefulSet esegue una replica. In modalità HA (abilitata impostando ha.enabled: true nei values), Postgres scala a tre repliche sotto Patroni, Redis scala a sei (tre primary, tre replica) e Redpanda scala a tre broker.

Cablaggio del data plane per Elido self-hosted: edge-redirect legge la cache Redis L2, api-core usa Postgres come source of truth ed emette eventi su Redpanda, click-ingester consuma Redpanda e scrive ClickHouse, analytics-api interroga ClickHouse, search interroga Meilisearch, e api-core archivia gli asset in MinIO

Caddy ingress#

Caddy gestisce la terminazione TLS e il TLS on-demand per i domini personalizzati dei tenant. Quando un operatore workspace aggiunge un dominio personalizzato tramite la dashboard (o l'API), domain-manager verifica la proprietà DNS, registra l'hostname come consentito e Caddy predispone il certificato Let's Encrypt alla prima richiesta HTTP su quell'hostname. Il chart deploya Caddy come DaemonSet sui nodi etichettati elido.app/ingress=true, oppure come Deployment nel setup predefinito single-node.

Il flusso per un dominio personalizzato del tenant go.company.com:

  1. L'operatore crea un CNAME: go.company.com → <your-k3s-ingress-ip> (o un record A che punta direttamente).
  2. L'operatore chiama POST /v1/workspaces/{id}/domains o clicca "Aggiungi dominio" nella dashboard.
  3. domain-manager interroga il DNS per confermare che il CNAME si risolva all'IP ingress.
  4. Caddy riceve la prima richiesta HTTPS, controlla l'allow-list emessa da Elido e richiede un certificato a Let's Encrypt tramite ACME HTTP-01.
  5. Il certificato viene archiviato nel volume di stato Caddy e rinnovato automaticamente.

Bootstrap alla prima esecuzione#

Dopo il completamento di helm install, l'API admin è disponibile ma non esiste alcun account utente.

Bootstrap dell'utente admin#

# Port-forward all'interfaccia admin di api-core (non esposta per default)
kubectl -n elido port-forward svc/api-core 8081:8081 &

# Crea il primo account admin
curl -X POST http://localhost:8081/internal/admin/bootstrap \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "[email protected]",
    "password": "your-secure-password"
  }'

L'endpoint di bootstrap è richiamabile solo dalla rete interna del cluster ed è disabilitato dopo la prima chiamata avvenuta con successo. Una volta che l'account admin esiste, accedi tramite la dashboard al tuo dominio configurato.

Crea il primo workspace#

Dopo aver effettuato l'accesso, la dashboard chiede di creare un workspace al primo utilizzo. In alternativa, tramite API:

curl -X POST https://links.example.com/v1/workspaces \
  -H "Authorization: Bearer <your-session-token>" \
  -H 'Content-Type: application/json' \
  -d '{"name": "My Workspace", "slug": "my-workspace"}'

Aggiungi un dominio personalizzato#

Dalle impostazioni del workspace, vai su Domini e aggiungi il tuo dominio per i link abbreviati. Imposta il record DNS (CNAME o A) prima di cliccare "Verifica" - domain-manager controlla il DNS immediatamente e restituisce un errore se il record non è ancora propagato. Superata la verifica, Caddy predisporrà il certificato alla prima richiesta di redirect su quel dominio. L'emissione del certificato richiede in genere 10–30 secondi alla prima richiesta.

Verifica che il TLS funzioni:

curl -I https://go.company.com/healthz
# Aspettati: HTTP/2 200

Considerazioni operative#

Backup#

Postgres. Il chart include un CronJob che esegue pg_dump su una pianificazione configurabile (default: giornaliero alle 02:00 UTC) e carica il dump compresso nel bucket MinIO configurato nei values. Per i deployment HA, pg_dump viene eseguito contro una replica per evitare impatti sul primary. Abilitalo nei values:

backups:
  postgres:
    enabled: true
    schedule: "0 2 * * *"
    retentionDays: 30
    s3Bucket: "elido-backups"

Per il point-in-time recovery, abilita l'archiviazione WAL (postgres.walArchive.enabled: true nei values), che spedisce i segmenti WAL a MinIO in modo continuo. Combina il pg_dump giornaliero con l'archiviazione WAL per un RPO inferiore a 5 minuti.

ClickHouse. ClickHouse archivia gli eventi di clic, che sono append-only e possono essere riprodotti da Redpanda se la retention lo consente. Il chart include un CronJob che esegue SQL BACKUP TABLE su MinIO utilizzando l'interfaccia di backup nativa di ClickHouse. Abilita con backups.clickhouse.enabled: true.

Redpanda. Redpanda è un bus di streaming, non un database. La retention è basata sul tempo (default: 7 giorni) e configurata nei values sotto redpanda.retention. Se il tuo click-ingester rimane indietro di più della finestra di retention, gli eventi vengono persi. Monitora il lag del consumer group - il chart include un alert Prometheus (ElidoRedpandaConsumerLag) che si attiva quando qualsiasi consumer group è indietro di più di 100K messaggi.

Verifica dei backup. Un backup che non è mai stato testato è un backup su cui non puoi fare affidamento. Esegui una prova di ripristino in un namespace separato almeno ogni trimestre:

kubectl -n elido-restore create ns elido-restore || true
helm install elido-restore ./deploy/helm/elido \
  --namespace elido-restore \
  --values ./my-values.yaml \
  --set restore.fromBackup=true \
  --set restore.backupDate="2026-05-01"

Monitoraggio#

Il chart include un ServiceMonitor per ogni servizio se il CRD Prometheus Operator è presente nel cluster. Metriche chiave su cui impostare alert:

monitoring:
  prometheus:
    enabled: true # richiede prometheus-operator nel cluster
  grafana:
    enabled: true # deploya dashboard predefinite
    adminPassword: "change-me"

Le dashboard Grafana predefinite coprono:

  • Latenza p50/p95 di edge-redirect, rapporto di hit della cache, volume di clic
  • Tasso di richieste di api-core, tasso di errori, latenza gRPC
  • Lag del consumer click-ingester per partizione Redpanda
  • Lag di replica primary/replica Postgres (Patroni)
  • Tasso di eviction Redis e pressione della memoria

Se hai già uno stack Prometheus nel cluster, imposta monitoring.prometheus.install: false e punta il tuo stack esistente ai ServiceMonitor.

Patroni HA per Postgres#

In modalità HA, Patroni gestisce l'elezione del leader e il failover. Il chart configura Patroni con il distributed configuration store kubernetes (usa Kubernetes ConfigMap, non è necessario un etcd separato). Il failover si completa tipicamente in 15–30 secondi. Durante il failover, api-core e billing sperimentano brevi errori di scrittura; entrambi i servizi riprovano su 5xx con backoff esponenziale.

Per ispezionare lo stato del cluster Patroni:

kubectl -n elido exec -it postgres-0 -- patronictl -c /etc/patroni/config.yml list

Flusso di aggiornamento#

Elido segue il versionamento semantico. Le release patch contengono correzioni di bug e non richiedono passaggi di migrazione manuale. Le release minor e major possono includere migrazioni dello schema del database, che sono incorporate nei binari dei servizi e vengono eseguite automaticamente all'avvio del pod tramite il migration runner.

Il percorso di aggiornamento consigliato:

# 1. Recupera il chart più recente (git pull nel repo clonato)
git pull origin main

# 2. Esamina le note di aggiornamento
helm show chart ./deploy/helm/elido | grep -A10 'version\|appVersion'

# 3. Esegui il diff dei cambiamenti ai values (richiede il plugin helm-diff)
helm diff upgrade elido ./deploy/helm/elido \
  --namespace elido \
  --values ./my-values.yaml

# 4. Aggiorna
helm upgrade elido ./deploy/helm/elido \
  --namespace elido \
  --values ./my-values.yaml \
  --wait --timeout 10m

Gestione delle migrazioni. Ogni binario del servizio Go contiene le sue migrazioni dello schema (usando golang-migrate contro una directory di migrazioni incorporata). All'avvio del pod, il binario esegue migrate up prima di servire le richieste. In un rolling update, il nuovo pod applica eventuali migrazioni in sospeso prima che il vecchio pod si termini. Le migrazioni devono essere backward-compatible con la versione minor precedente - una nuova colonna aggiunta nella v1.5 deve essere nullable o avere un default in modo che il binario v1.4 in esecuzione accanto ad essa durante la finestra di rollout non produca errori.

Blue/green per gli aggiornamenti major. Per gli aggiornamenti di versione major in cui i cambiamenti dello schema non sono backward-compatible, usa la strategia blue/green: installa la nuova versione sotto un nome di release Helm separato in un namespace staging, migra uno snapshot del database di produzione in essa, esegui lo smoke test, poi esegui il DNS cutover a livello ingress. Dopo aver validato lo stack green, elimina la release blue.

# Installa lo stack green
helm install elido-green ./deploy/helm/elido \
  --namespace elido-green \
  --create-namespace \
  --values ./green-values.yaml

# Dopo la validazione, cambia il DNS al tuo registrar
# Poi decommissiona il blue
helm uninstall elido --namespace elido

Quando non fare il self-hosting#

Il self-hosting è la scelta giusta in un insieme ristretto di circostanze. In più casi di quanto potresti aspettarti, è quella sbagliata.

Il tuo workload è piccolo. Se crei meno di 100.000 link al mese e non hai requisiti stretti di residenza dei dati, Elido managed costa meno in denaro e tempo operativo rispetto all'esecuzione di k3s. Il tier hosted include backup, aggiornamenti e on-call per l'infrastruttura che altrimenti possederesti.

Non hai esperienza operativa con Kubernetes. k3s è minimale, ma Kubernetes non è semplice. Se nessuno del tuo team ha operato StatefulSet, gestito il backup/ripristino di etcd o eseguito il debug di un CrashLoopBackOff in Patroni alle 2 di notte, il self-hosting aggiunge una categoria di rischio operativo che i risparmi sui costi dell'infrastruttura non compensano.

Il tuo requisito di compliance è la residenza dei dati UE, non la tenancy specifica. Residenza dei dati UE significa dati archiviati ed elaborati nell'UE. L'infrastruttura managed di Elido gira su Hetzner FRA e OVH GRA, entrambi i quali soddisfano i requisiti dell'articolo 44 del GDPR senza trasferimento transfrontaliero. Se il requisito effettivo del tuo team di compliance è "dati nell'UE", il prodotto managed lo soddisfa già senza un deployment self-hosted - vedi la pagina dei prezzi per le funzionalità di compliance disponibili su ogni piano.

Vuoi performance edge multi-regione. Il servizio managed esegue POP edge a Francoforte, Ashburn e Singapore, con l'edge-redirect hot-path deployato in ognuno. Un singolo cluster k3s self-hosted in una regione ha una latenza di redirect limitata dalla distanza geografica della VM dall'utente finale. Il self-hosting multi-regione è possibile ma moltiplica significativamente la superficie operativa - è un'impresa diversa da ciò che questa guida copre.


Le domande sui valori del chart Helm, i requisiti di risorse degli StatefulSet per volumi di traffico specifici o i percorsi di aggiornamento per una versione specifica dovrebbero essere inviate alla GitHub Discussions board per l'edizione self-hosted. Per le domande legate alla compliance - specificamente cosa fornisce la configurazione self-hosted oltre alla residenza UE - la pagina compliance copre in dettaglio il log di audit, l'export delle prove e i controlli RBAC.

Correlati sul blog#

Prova Elido

Incolla un URL, ottieni un link breve

Senza registrazione. Il link vive 30 giorni. Iscriviti per conservarlo.

Gratis, nessuna registrazione richiesta · 2 al giorno

Prova Elido

Accorciatore di URL ospitato nell'UE: domini personalizzati, analisi approfondite e API aperta. Piano gratuito - senza carta di credito.

Tag
self hosted url shortener
k3s
kubernetes url shortener
helm
self host elido
url shortener kubernetes
data residency
eu compliance

Continua a leggere