La versión gestionada de Elido se ejecuta en infraestructura de la región UE con una configuración privacy-by-default. Para la mayoría de los usuarios, eso es suficiente. Para algunos, no lo es.
Si su equipo de seguridad requiere que los datos de destino de los enlaces cortos y los eventos de clic nunca abandonen un centro de datos específico, si su política de auditoría exige control total sobre el servidor de base de datos, o si está construyendo Elido como parte de una plataforma interna que debe ejecutarse air-gapped, la ruta self-hosted existe exactamente para ese propósito.
Esta guía recorre el despliegue de Elido en k3s, una distribución mínima de Kubernetes de grado producción que se ejecuta cómodamente en una sola VM o un pequeño clúster HA. Al final tendrá todos los servicios ejecutándose, TLS aprovisionado, backups cableados y una ruta de actualización repetible. La guía asume que quiere un sistema funcional, no un tutorial sobre conceptos de Kubernetes - las explicaciones se mantienen cortas y los pasos operativos son explícitos.
Por qué hacer self-hosting#
Antes de entrar en el playbook, vale la pena ser preciso sobre las compensaciones. El self-hosting no es automáticamente mejor - mueve el riesgo operativo de un proveedor a su propio equipo.
Residencia de datos y cumplimiento. Si su marco de cumplimiento (alcance ISO 27001, política interna de clasificación de datos o cláusula contractual de residencia de datos) requiere que los metadatos de enlaces y los eventos de analítica permanezcan en infraestructura que usted controle directamente, el SaaS gestionado no puede satisfacer ese requisito independientemente de dónde se ejecute físicamente. Un despliegue self-hosted en una VM de región UE que usted posee sí lo hace. Consulte la visión general de cumplimiento para detalles sobre cómo la arquitectura de Elido se mapea a marcos comunes.
Previsibilidad de costes a escala. Los planes gestionados se cobran por enlaces activos y volumen de clics. Por encima de cierto umbral -típicamente en algún punto por encima de unos pocos millones de clics al mes- el coste por evento en los planes gestionados supera el coste de infraestructura de ejecutar la carga StatefulSet equivalente por su cuenta. El punto de cruce depende de la forma de su tráfico, pero existe.
Control de auditoría. Algunas organizaciones requieren acceso a las tablas Postgres en bruto y a los datos de eventos de clic de ClickHouse para empaquetar evidencia, retención legal o integración SIEM. La API de Elido expone endpoints de registro de auditoría y exportaciones de evidencia, pero el acceso directo a la base de datos solo está disponible en despliegues self-hosted.
A qué está renunciando. k3s requiere que alguien sea propietario de las actualizaciones, la salud de los nodos, la verificación de backups y la respuesta a incidentes. Si su equipo no tiene experiencia operativa con Kubernetes, la versión gestionada en una ubicación de hosting cumplidora es casi con certeza la respuesta correcta. Consulte la última sección de esta publicación para un tratamiento más directo de cuándo el self-hosting es la decisión equivocada.
Requisitos previos#
- Un clúster k3s. Un solo nodo con 4 vCPU y 8 GB de RAM maneja cargas ligeras. Para HA, tres nodos del plano de control más dos o más nodos worker es la topología mínima recomendada. El etcd integrado de k3s cubre el HA del plano de control; el HA del plano de datos lo maneja Patroni (Postgres) dentro del chart de Helm.
kubectlconfigurado con un contexto que apunte a su clúster.- Helm 3.14 o posterior.
- Un dominio que controle con la capacidad de crear registros DNS. La IP de ingreso de k3s debe ser accesible en los puertos 80 y 443 desde internet para que los desafíos ACME de Let's Encrypt tengan éxito.
- Si está impulsado por cumplimiento, una VM de región UE. Hetzner (Falkenstein, Helsinki, Núremberg) y OVH (Gravelines, Roubaix) son los dos proveedores que utiliza la propia infraestructura edge de Elido.
En una Hetzner CX32 nueva (4 vCPU / 8 GB) ejecutando Ubuntu 24.04, k3s se instala en aproximadamente 30 segundos:
curl -sfL https://get.k3s.io | sh -
# Copy the kubeconfig to your local machine:
scp root@<your-vm>:/etc/rancher/k3s/k3s.yaml ~/.kube/elido-self-host.yaml
export KUBECONFIG=~/.kube/elido-self-host.yaml
kubectl get nodes # should show Ready
Clone el repositorio de Elido para obtener el chart de Helm (el chart vive en deploy/helm/elido/ en el repo - no hay un repositorio público de Helm separado):
git clone https://github.com/elidoapp/elido.git
cd elido
Inicio rápido#
El preset self-host (deploy/helm/elido/values-selfhost.yaml) es el punto de partida recomendado para un despliegue k3s de un solo nodo. Genere los secrets primero, luego instale:
# 1. Mint all required Kubernetes Secrets in one shot (idempotent)
./scripts/bootstrap-secrets.sh elido
# 2. Install using the self-host preset
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
Para overrides personalizados (recuentos de réplicas de producción, servicios externos gestionados, etc.), copie values-selfhost.yaml a un my-values.yaml local, edítelo y páselo con -f. Las secciones clave de valores de nivel superior son ingress.hosts.* (sus nombres de host), image.registry/image.tag y los bloques resources por servicio. No use global.domain - el chart usa ingress.hosts.* para la configuración de dominio.
La bandera --wait bloquea hasta que todos los Deployments y StatefulSets alcancen su estado listo. En un nodo nuevo sin imágenes en caché, espere entre 5 y 8 minutos en un enlace de 1 Gbps.
Compruebe que todo se levantó:
kubectl -n elido get pods
kubectl -n elido get svc
Vea la referencia de valores del chart con helm show values ./deploy/helm/elido para la superficie completa de parámetros. El deploy/helm/elido/README.md en el repositorio es la fuente autoritativa para nombres de campos específicos y el uso del script de bootstrap.
Arquitectura que está desplegando#
La arquitectura de Elido se divide por presupuesto de latencia. Comprender esa división le ayuda a tomar decisiones informadas de asignación de recursos.
Ruta caliente: edge-redirect#
edge-redirect es el único servicio en la ruta sincrónica de una solicitud de redirección. Está escrito en Go con fasthttp y tiene un presupuesto de latencia estricto: p50 5 ms, p95 15 ms en un cache hit. El servicio mantiene una caché de dos niveles: un LRU en proceso (L1) respaldado por Redis Cluster (L2). En un cache miss, cae a una llamada gRPC a api-core. Los eventos de clic se emiten fire-and-forget a Redpanda - la respuesta de redirección nunca se mantiene esperando a que se complete la escritura del evento.
El chart de Helm despliega edge-redirect como un Deployment con un HorizontalPodAutoscaler. En una configuración self-hosted de una sola región, dos réplicas es un punto de partida razonable. La caché Redis L2 se comparte entre réplicas, por lo que el calentamiento de la caché es rápido después de una actualización rolling.
Ruta tibia: superficie API#
Cinco servicios manejan trabajo sincrónico de API y lógica de negocio:
api-core- Go + chi, REST y gRPC. Fuente de verdad para enlaces, workspaces, membresías, dominios personalizados, eventos de auditoría.api-bff- Capa BFF Node/Hono para el panel web y clientes móviles. Agrega a través deapi-coreyanalytics-api.analytics-api- Go, consultas a ClickHouse. Sirve los paneles de analítica.billing- Go + chi + sqlc. Motor de IVA UE, integración de pagos LiqPay, generación de facturas.search- Go, hace proxy de las consultas de búsqueda de enlaces a Meilisearch.
La autenticación la maneja Ory Kratos (identidad, sesiones, verificación de email) y Ory Hydra (tokens OAuth2/OIDC para integraciones de terceros y la extensión del navegador). Ambos se despliegan como Deployments en el chart.
Ruta fría: workers asíncronos#
Seis servicios consumen eventos de los tópicos de Redpanda y no tienen presupuesto de tiempo de respuesta:
click-ingester- consume eventos de clic, escribe en ClickHouse.webhook-dispatcher- distribuye payloads de webhook firmados a endpoints de clientes.notification- notificaciones por email e in-app (eventos de cuenta, alertas de enlaces).url-scanner- ejecuta escaneo de URL de destino contra Google Safe Browsing, PhishTank, SURBL.metadata-fetcher- obtiene metadatos Open Graph para previsualizaciones de enlaces.domain-manager- verificación DNS y aprovisionamiento Caddy on-demand TLS para dominios personalizados.
Plano de datos: StatefulSets#
Seis sistemas con estado respaldan los servicios anteriores:
| Sistema | Rol | Recurso del chart |
|---|---|---|
| Postgres (Patroni) | Fuente de verdad para enlaces, usuarios, facturación | StatefulSet, 3 réplicas en modo HA |
| Redis Cluster | Caché de enlaces de ruta caliente | StatefulSet |
| ClickHouse | Almacenamiento de eventos de clic y consultas de analítica | StatefulSet |
| Redpanda | Bus de eventos entre servicios | StatefulSet |
| MinIO | Activos subidos por usuarios (imágenes QR, exports) | StatefulSet |
| Meilisearch | Búsqueda de enlaces in-app | StatefulSet |
En un despliegue de un solo nodo, cada StatefulSet ejecuta una réplica. En modo HA (habilitado configurando ha.enabled: true en values), Postgres escala a tres réplicas bajo Patroni, Redis escala a seis (tres primarios, tres réplicas) y Redpanda escala a tres brokers.
Caddy ingress#
Caddy maneja la terminación TLS y on-demand TLS para dominios personalizados de inquilinos. Cuando un operador de workspace añade un dominio personalizado a través del panel (o la API), domain-manager verifica la propiedad DNS, registra el nombre de host como permitido, y Caddy aprovisiona el certificado de Let's Encrypt en la primera solicitud HTTP a ese nombre de host. El chart despliega Caddy como un DaemonSet en nodos etiquetados elido.app/ingress=true, o como un Deployment en la configuración predeterminada de un solo nodo.
El flujo para un dominio personalizado de inquilino go.company.com:
- El operador crea un CNAME:
go.company.com → <your-k3s-ingress-ip>(o un registro A apuntando directamente). - El operador llama a
POST /v1/workspaces/{id}/domainso hace clic en "Add domain" en el panel. domain-managerconsulta DNS para confirmar que el CNAME se resuelve a la IP de ingreso.- Caddy recibe la primera solicitud HTTPS, verifica la lista de permitidos emitida por Elido y solicita un certificado a Let's Encrypt vía ACME HTTP-01.
- El certificado se almacena en el volumen de estado de Caddy y se renueva automáticamente.
Bootstrap de primera ejecución#
Después de que helm install se complete, la API de administración está disponible pero no existe ninguna cuenta de usuario.
Bootstrap del usuario admin#
# Port-forward to the api-core admin interface (not exposed by default)
kubectl -n elido port-forward svc/api-core 8081:8081 &
# Create the first admin account
curl -X POST http://localhost:8081/internal/admin/bootstrap \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"password": "your-secure-password"
}'
El endpoint de bootstrap solo se puede llamar desde dentro de la red del clúster y se deshabilita después de la primera llamada exitosa. Una vez que la cuenta de admin existe, inicie sesión a través del panel en su dominio configurado.
Crear el primer workspace#
Después de iniciar sesión, el panel solicita la creación del workspace en el primer uso. Alternativamente, vía 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"}'
Añadir un dominio personalizado#
Desde la configuración del workspace, vaya a Domains y añada su dominio de enlace corto. Configure el registro DNS (CNAME o A) antes de hacer clic en "Verify" - domain-manager verifica DNS de inmediato y devuelve un error si el registro aún no se ha propagado. Al pasar la verificación, Caddy aprovisionará el certificado en la primera solicitud de redirección a ese dominio. La emisión del certificado típicamente tarda entre 10 y 30 segundos en la primera solicitud.
Verifique que TLS funciona:
curl -I https://go.company.com/healthz
# Expect: HTTP/2 200
Preocupaciones operativas#
Backups#
Postgres. El chart envía un CronJob que ejecuta pg_dump en una programación configurable (predeterminado: diariamente a las 02:00 UTC) y sube el volcado comprimido al bucket de MinIO configurado en values. Para despliegues HA, pg_dump se ejecuta contra una réplica para evitar impactar al primario. Actívelo en values:
backups:
postgres:
enabled: true
schedule: "0 2 * * *"
retentionDays: 30
s3Bucket: "elido-backups"
Para recuperación point-in-time, habilite el archivado de WAL (postgres.walArchive.enabled: true en values), que envía segmentos WAL a MinIO continuamente. Combine pg_dump diario con archivado WAL para un RPO inferior a 5 minutos.
ClickHouse. ClickHouse almacena eventos de clic, que son append-only y se pueden reproducir desde Redpanda si la retención lo permite. El chart incluye un CronJob que ejecuta SQL BACKUP TABLE a MinIO usando la interfaz nativa de backup de ClickHouse. Active con backups.clickhouse.enabled: true.
Redpanda. Redpanda es un bus de streaming, no una base de datos. La retención es basada en tiempo (predeterminado: 7 días) y se configura en values bajo redpanda.retention. Si su click-ingester se queda atrás más allá de la ventana de retención, los eventos se pierden. Monitoree el lag del grupo de consumidores - el chart envía una alerta de Prometheus (ElidoRedpandaConsumerLag) que se dispara cuando cualquier grupo de consumidores está más de 100K mensajes atrasado.
Verificación de backup. Un backup que nunca se ha probado es un backup del que no puede depender. Ejecute un ejercicio de restauración en un namespace separado al menos trimestralmente:
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"
Monitoreo#
El chart incluye un ServiceMonitor para cada servicio si el CRD del Prometheus Operator está presente en el clúster. Métricas clave sobre las que alertar:
monitoring:
prometheus:
enabled: true # requires prometheus-operator in cluster
grafana:
enabled: true # deploys bundled dashboards
adminPassword: "change-me"
Los paneles de Grafana incluidos cubren:
- Latencia p50/p95 de
edge-redirect, ratio de cache hit, volumen de clics - Tasa de solicitudes de
api-core, tasa de error, latencia gRPC - Lag de consumidor de
click-ingesterpor partición de Redpanda - Lag de replicación primario/réplica de Postgres (Patroni)
- Tasa de eviction de Redis y presión de memoria
Si ya tiene un stack de Prometheus en el clúster, establezca monitoring.prometheus.install: false y apunte su stack existente a los ServiceMonitors.
Patroni HA para Postgres#
En modo HA, Patroni gestiona la elección de líder y el failover. El chart configura Patroni con el almacén de configuración distribuida kubernetes (usa ConfigMaps de Kubernetes, no se necesita etcd separado). El failover típicamente se completa en 15-30 segundos. Durante el failover, api-core y billing experimentan errores breves de escritura; ambos servicios reintentan en 5xx con backoff exponencial.
Para inspeccionar el estado del clúster Patroni:
kubectl -n elido exec -it postgres-0 -- patronictl -c /etc/patroni/config.yml list
Flujo de actualización#
Elido sigue versionado semántico. Las versiones patch contienen correcciones de bugs y no requieren pasos de migración manuales. Las versiones minor y major pueden incluir migraciones de esquema de base de datos, que están incrustadas en los binarios del servicio y se ejecutan automáticamente en el arranque del pod a través del runner de migración.
La ruta de actualización recomendada:
# 1. Pull the latest chart (git pull in your cloned repo)
git pull origin main
# 2. Review the upgrade notes
helm show chart ./deploy/helm/elido | grep -A10 'version\|appVersion'
# 3. Diff the values changes (requires helm-diff plugin)
helm diff upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml
# 4. Upgrade
helm upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml \
--wait --timeout 10m
Manejo de migraciones. Cada binario de servicio Go contiene sus migraciones de esquema (usando golang-migrate contra un directorio de migraciones incrustado). En el arranque del pod, el binario ejecuta migrate up antes de servir solicitudes. En una actualización rolling, el nuevo pod aplica cualquier migración pendiente antes de que el pod antiguo termine. Las migraciones deben ser compatibles hacia atrás con la versión minor anterior - una nueva columna añadida en v1.5 debe ser nullable o llevar un valor predeterminado para que el binario v1.4 ejecutándose junto a él durante la ventana de rollout no falle.
Blue/green para actualizaciones major. Para actualizaciones de versión major donde los cambios de esquema no son compatibles hacia atrás, use la estrategia blue/green: instale la nueva versión bajo un nombre de release de Helm separado en un namespace de staging, migre una instantánea de la base de datos de producción a él, haga smoke-test, luego ejecute el corte de DNS a nivel de ingreso. Después de validar el stack verde, elimine el release azul.
# Install green stack
helm install elido-green ./deploy/helm/elido \
--namespace elido-green \
--create-namespace \
--values ./green-values.yaml
# After validation, cut over DNS at your registrar
# Then decommission blue
helm uninstall elido --namespace elido
Cuándo no hacer self-hosting#
El self-hosting es la decisión correcta en un conjunto limitado de circunstancias. En más casos de los que podría esperar, es la equivocada.
Su carga de trabajo es pequeña. Si crea menos de 100K enlaces al mes y no tiene requisitos estrictos de residencia de datos, Elido gestionado cuesta menos en dinero y tiempo operativo que ejecutar k3s. El nivel hosteado incluye backups, actualizaciones y on-call para la infraestructura que de otro modo poseería.
No tiene experiencia operativa con Kubernetes. k3s es mínimo, pero Kubernetes no es simple. Si nadie en su equipo ha operado StatefulSets, manejado backup/restore de etcd o depurado un CrashLoopBackOff en Patroni a las 2 AM, el self-hosting añade una categoría de riesgo operativo que los ahorros en costes de infraestructura no compensan.
Su requisito de cumplimiento es residencia de datos de la UE, no tenencia específica. Residencia de datos de la UE significa datos almacenados y procesados en la UE. La infraestructura gestionada de Elido se ejecuta en Hetzner FRA y OVH GRA, ambos satisfacen los requisitos del artículo 44 del GDPR sin transferencia transfronteriza. Si el requisito real de su equipo de cumplimiento es "datos en la UE", el producto gestionado ya lo satisface sin un despliegue self-hosted - consulte la página de precios para las características de cumplimiento disponibles en cada plan.
Quiere rendimiento edge multi-región. El servicio gestionado ejecuta POPs edge en Frankfurt, Ashburn y Singapur, con el edge-redirect de ruta caliente desplegado en cada uno. Un clúster k3s self-hosted de una sola región tiene latencia de redirección limitada por la distancia geográfica de la VM al usuario final. El self-hosting multi-región es posible pero multiplica significativamente la superficie operativa - es un esfuerzo diferente del que cubre esta guía.
Las preguntas sobre los values del chart de Helm, los requisitos de recursos de StatefulSets para volúmenes de tráfico específicos o las rutas de actualización para una versión específica deberían ir al tablero de GitHub Discussions para la edición self-hosted. Para preguntas relacionadas con cumplimiento - específicamente qué proporciona la configuración self-hosted más allá de la residencia UE - la página de cumplimiento cubre el registro de auditoría, la exportación de evidencia y los controles RBAC en detalle.
Relacionados en el blog#
Prueba Elido
Pega una URL, obtén un enlace corto
Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.
Gratis, sin registro · 2 por día