<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: igcodinap</title>
    <description>The latest articles on DEV Community by igcodinap (@igcodinap).</description>
    <link>https://dev.to/igcodinap</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F227033%2F720053a1-4ceb-4eb7-92f9-f7de7511d80e.jpeg</url>
      <title>DEV Community: igcodinap</title>
      <link>https://dev.to/igcodinap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/igcodinap"/>
    <language>en</language>
    <item>
      <title>Migrando de Supabase a una arquitectura full Go: La poesía de los binarios estáticos</title>
      <dc:creator>igcodinap</dc:creator>
      <pubDate>Tue, 13 Jan 2026 14:26:48 +0000</pubDate>
      <link>https://dev.to/gopherscl/migrando-de-supabase-a-una-arquitectura-full-go-la-poesia-de-los-binarios-estaticos-145n</link>
      <guid>https://dev.to/gopherscl/migrando-de-supabase-a-una-arquitectura-full-go-la-poesia-de-los-binarios-estaticos-145n</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo el ecosistema Go nos permitió reemplazar un backend hosted por una infraestructura self-hosted elegante, mantenible y completamente gratuita.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Hace unos meses tomamos una decisión que parecía arriesgada: migrar una aplicación desde Supabase hosted hacia una arquitectura completamente self-hosted. El resultado fue inesperadamente hermoso.&lt;/p&gt;

&lt;h2&gt;
  
  
  El contexto
&lt;/h2&gt;

&lt;p&gt;Teníamos una instancia Supabase para autenticación y base de datos. Funcionaba bien, pero dependíamos de sus restricciones y limitaciones  para seguir creando features. Cuando el proyecto escaló, se hizo cada vez más difícil mantenerlo y añadir nuevos features debido al mismo diseño de supabase. Por esto, decidimos tomar control total de nuestra infraestructura.&lt;/p&gt;

&lt;p&gt;Lo que no esperábamos era descubrir que todo el ecosistema que necesitábamos estaba escrito en Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  La arquitectura resultante
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌─────────────────────────────────────────┐
                    │         GCP Compute Engine VM           │
                    │                                         │
    Internet ───────┤  Caddy ──▶ go-app ──▶ Cloud SQL Proxy  │──▶ PostgreSQL
                    │    │                        │           │
                    │    └─────▶ GoTrue ──────────┘           │
                    │                                         │
                    │         Alloy ──▶ Grafana Cloud         │
                    └─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cinco servicios. &lt;strong&gt;Todos escritos en Go.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Propósito&lt;/th&gt;
&lt;th&gt;Creador&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;go-app&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nuestra API REST&lt;/td&gt;
&lt;td&gt;Nosotros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caddy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reverse proxy + TLS automático&lt;/td&gt;
&lt;td&gt;Matt Holt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GoTrue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Autenticación (signup, login, OAuth)&lt;/td&gt;
&lt;td&gt;Supabase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alloy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Métricas y logs&lt;/td&gt;
&lt;td&gt;Grafana Labs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloud SQL Proxy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Conexión segura a Cloud SQL&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  ¿Por qué esto es especial?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Binarios estáticos
&lt;/h3&gt;

&lt;p&gt;Cada uno de estos servicios compila a un único binario sin dependencias externas. No hay runtime que instalar, no hay versiones de Node que gestionar, no hay virtualenvs. Un binario, un proceso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Así de simple es desplegar Caddy&lt;/span&gt;
wget https://github.com/caddyserver/caddy/releases/download/v2.8.4/caddy_2.8.4_linux_amd64.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; caddy_2.8.4_linux_amd64.tar.gz
./caddy run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Consumo de recursos mínimo
&lt;/h3&gt;

&lt;p&gt;Nuestra VM de staging corre los 5 servicios con &lt;strong&gt;2GB de RAM&lt;/strong&gt;. En idle, el consumo total no supera los 400MB. Comparen eso con correr un cluster de Node.js + Redis + Nginx.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Configuración declarativa
&lt;/h3&gt;

&lt;p&gt;Caddy, GoTrue y Alloy usan archivos de configuración simples y legibles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Caddyfile - 40 líneas para TLS + reverse proxy + CORS
api.dominio.com {
  handle /auth/v1/* {
    reverse_proxy gotrue:9999
  }
  handle {
    reverse_proxy api:8080
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. El mismo idioma mental
&lt;/h3&gt;

&lt;p&gt;Cuando debuggeas un problema, saltas entre servicios pero te mantienes en el mismo paradigma. Context propagation, structured logging, graceful shutdown - todos siguen los mismos patrones porque todos son Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  GoTrue: El corazón de la autenticación
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/supabase/auth" rel="noopener noreferrer"&gt;GoTrue&lt;/a&gt; es el servicio de autenticación de Supabase, pero es completamente standalone. Lo puedes correr con Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;supabase/auth:v2.184.0&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;GOTRUE_DB_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://user:pass@db:5432/mydb?search_path=auth&lt;/span&gt;
    &lt;span class="na"&gt;GOTRUE_JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${JWT_SECRET}&lt;/span&gt;
    &lt;span class="na"&gt;GOTRUE_SITE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://miapp.com&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gotrue&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migrate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;gotrue&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;serve"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y obtienes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signup/login con email y password&lt;/li&gt;
&lt;li&gt;Magic links&lt;/li&gt;
&lt;li&gt;OAuth (Google, Apple, GitHub, etc.)&lt;/li&gt;
&lt;li&gt;Refresh tokens&lt;/li&gt;
&lt;li&gt;JWT estándar compatible con cualquier middleware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo mejor: &lt;strong&gt;es el mismo servicio que usa Supabase en producción&lt;/strong&gt;. No es una versión recortada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caddy: TLS sin dolor
&lt;/h2&gt;

&lt;p&gt;Si nunca han usado Caddy, prepárense para olvidar todo lo que saben sobre configurar certificados SSL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api.miapp.com {
  reverse_proxy localhost:8080
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso es todo. Caddy obtiene certificados de Let's Encrypt automáticamente, los renueva, y configura HTTPS con los headers de seguridad correctos. Cero configuración adicional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alloy: Observabilidad moderna
&lt;/h2&gt;

&lt;p&gt;Grafana Alloy (antes Grafana Agent) recolecta métricas y logs y los envía a Grafana Cloud (que tiene un tier gratuito generoso).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Recolectar logs de todos los containers Docker
loki.source.docker "containers" {
  host = "unix:///var/run/docker.sock"
  targets = discovery.docker.containers.targets
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con 20 líneas de configuración tienes logs centralizados de todos tus servicios, con labels automáticos por container.&lt;/p&gt;

&lt;h2&gt;
  
  
  El flujo de autenticación
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usuario                    Frontend                   Caddy                    GoTrue                     GoApp
   │                          │                         │                         │                         │
   │──── Login ──────────────▶│                         │                         │                         │
   │                          │─── POST /auth/v1/token ▶│                         │                         │
   │                          │                         │──── proxy ─────────────▶│                         │
   │                          │                         │                         │── validate credentials ─│
   │                          │                         │◀─── JWT ────────────────│                         │
   │                          │◀── { access_token } ────│                         │                         │
   │◀─── Logged in ───────────│                         │                         │                         │
   │                          │                         │                         │                         │
   │──── Get entity ─────────▶│                         │                         │                         │
   │                          │── GET /api/entity ─────▶│                         │                         │
   │                          │   Authorization: Bearer │──── proxy ──────────────────────────────────────▶│
   │                          │                         │                         │                         │── validate JWT
   │                          │                         │                         │                         │── query DB
   │                          │◀──────────────────────────────────────────────────── { entity } ───────────│
   │◀─── entity ──────────────│                         │                         │                         │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;entity: cualquiera sea el recurso de tu db que quieras consumir.&lt;/p&gt;

&lt;p&gt;El JWT que emite GoTrue es validado por nuestro middleware en go-app. Mismo secreto, mismo formato, cero fricción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Costos
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Costo mensual&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VM e2-small (staging)&lt;/td&gt;
&lt;td&gt;~$15 USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud SQL (db-f1-micro)&lt;/td&gt;
&lt;td&gt;~$10 USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana Cloud&lt;/td&gt;
&lt;td&gt;$0 (tier gratuito)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caddy, GoTrue, Alloy&lt;/td&gt;
&lt;td&gt;$0 (open source)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total staging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$25 USD&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para producción duplicamos los recursos, pero seguimos muy por debajo de lo que pagábamos por Supabase Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendimos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Self-hosted no significa complejo
&lt;/h3&gt;

&lt;p&gt;Con las herramientas correctas, self-hosted puede ser más simple que managed. Un &lt;code&gt;docker-compose up -d&lt;/code&gt; y tienes todo corriendo.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Go es infraestructura
&lt;/h3&gt;

&lt;p&gt;El ecosistema Go para herramientas de infraestructura es impresionante: Docker, Kubernetes, Terraform, Prometheus, Grafana, Caddy, Traefik, Hugo... La lista es interminable.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Supabase hizo bien su trabajo
&lt;/h3&gt;

&lt;p&gt;GoTrue es un proyecto de calidad. Está bien documentado, es estable, y hace una cosa bien. Que sea open source y self-hosteable habla bien de Supabase como empresa.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. La cohesión tiene valor
&lt;/h3&gt;

&lt;p&gt;Hay algo satisfactorio en ver &lt;code&gt;docker ps&lt;/code&gt; y saber que todos esos procesos comparten el mismo ADN. Cuando algo falla, el debugging sigue los mismos patrones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/supabase/auth" rel="noopener noreferrer"&gt;GoTrue GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/oss/alloy/" rel="noopener noreferrer"&gt;Grafana Alloy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huma.rocks/" rel="noopener noreferrer"&gt;Huma v2&lt;/a&gt; - Framework HTTP para Go que usamos en la go-app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Si están considerando migrar de un servicio hosted a self-hosted, el ecosistema Go les tiene cubiertos. No necesitan reinventar la rueda - solo necesitan ensamblar las piezas correctas.&lt;/p&gt;

&lt;p&gt;Y si ya programan en Go, van a sentirse como en casa. &lt;/p&gt;




&lt;p&gt;&lt;em&gt;¿Preguntas sobre la migración o la arquitectura? Déjenlas en los comentarios o encuéntrenme en la comunidad de Golang Chile.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>infrastructure</category>
      <category>supabase</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
