Cómo Ahorré 48 al Año con Newsletter Self-Hosted (Y Por Qué Deberías Considerarlo)

Cómo Ahorré $348 al Año con Newsletter Self-Hosted (Y Por Qué Deberías Considerarlo)

Pago $0/mes por mi newsletter. Antes pagaba $29/mes en MailerLite.

Ahorro anual: $348.

Suscriptores: 1,923.

Open rate: 32% (vs 21% promedio industria).

No es que MailerLite sea malo. Es excelente. El problema es que no necesitas pagar por algo que puedes self-hostear gratis.

Te cuento cómo migré de MailerLite a Listmonk, cuánto tiempo tomó, y por qué mi open rate subió después de la migración.


El Problema: SaaS de Newsletters es Caro

Pricing típico (2026):

  • MailerLite: $29/mes para 1,923 subs
  • Mailchimp: $45/mes
  • ConvertKit: $66/mes

Total anual: $348-792.

Para qué?

  • Enviar emails
  • Templates básicos
  • Stats (opens, clicks)

Realidad: Puedes hacer TODO eso con software open source + $12/mes VPS (que ya tienes para otros servicios).


La Solución: Listmonk (Self-Hosted)

Listmonk es open source newsletter platform:

  • ✅ Campañas ilimitadas
  • ✅ Suscriptores ilimitados
  • ✅ Templates customizables
  • ✅ Stats completas (opens, clicks, bounces)
  • ✅ Multi-lista
  • ✅ Segmentación
  • ✅ API completa
  • ✅ Docker-ready

Costo: $0/mes (solo hosting, que ya tienes).

Setup time: 2 horas (one-time).


La Migración: MailerLite → Listmonk

1. Export de MailerLite

MailerLite dashboard:

  • Subscribers → Export → CSV
  • Campaigns → Export stats (opcional)

Output: subscribers.csv con:

  • Email
  • Name
  • Status (active, unsubscribed, bounced)
  • Subscribed date

2. Setup Listmonk (Docker)

docker-compose.yml:

version: '3.8'

services:
  # PostgreSQL (database)
  postgres:
    image: postgres:15
    container_name: listmonk_db
    restart: always
    environment:
      POSTGRES_USER: listmonk
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: listmonk
    volumes:

      - postgres_data:/var/lib/postgresql/data
    networks:

      - listmonk_net

  # Listmonk
  listmonk:
    image: listmonk/listmonk:latest
    container_name: listmonk
    restart: always
    ports:

      - "9000:9000"
    depends_on:

      - postgres
    environment:
      LISTMONK_app__address: "0.0.0.0:9000"
      LISTMONK_db__host: postgres
      LISTMONK_db__port: 5432
      LISTMONK_db__user: listmonk
      LISTMONK_db__password: ${POSTGRES_PASSWORD}
      LISTMONK_db__database: listmonk
      LISTMONK_app__admin_username: ${ADMIN_USER}
      LISTMONK_app__admin_password: ${ADMIN_PASSWORD}
    networks:

      - listmonk_net

volumes:
  postgres_data:

networks:
  listmonk_net:
    driver: bridge

Levantar:

cd /home/user/listmonk
docker-compose up -d

Acceder: http://your-server:9000


3. Reverse Proxy con Caddy

Caddyfile:

listmonk.yourdomain.com {
    reverse_proxy localhost:9000

    header {
        Strict-Transport-Security "max-age=31536000;"
    }
}

Reload Caddy:

caddy reload

SSL: Caddy lo hace automático (Let’s Encrypt).

Resultado: https://listmonk.yourdomain.com con SSL.


4. Import Subscribers

Listmonk UI:

  1. Crear lista («Newsletter Personal»)
  2. Subscribers → Import
  3. Upload subscribers.csv
  4. Map columns: Email → email, Name → name
  5. Status: Active (only import active subs)

Resultado: 1,923 subs importados en 30 segundos.


5. Crear Primera Campaña

Listmonk → Campaigns → New Campaign:

  • Name: «Test Migration»
  • Subject: «Nueva plataforma, mismo contenido»
  • Template: Default (puedes customizar después)
  • List: Newsletter Personal
  • Schedule: Ahora o futuro

Body (plain text o HTML):

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Georgia, serif; max-width: 600px; margin: 0 auto; padding: 20px; }
    h1 { font-size: 24px; margin-bottom: 10px; }
    p { line-height: 1.6; margin-bottom: 15px; }
  </style>
</head>
<body>
  <h1>Hola {{ .Subscriber.FirstName }},</h1>

  <p>Migré la newsletter a nueva plataforma (self-hosted, más control).</p>

  <p>Nada cambia para ti. Mismo contenido, misma frecuencia.</p>

  <p>Si tienes problemas recibiendo emails, avísame.</p>

  <p>Gracias por leer,<br>Cristian</p>

  <p style="color: #666; font-size: 14px; margin-top: 40px;">
    <a href="{{ UnsubscribeURL }}">Unsubscribe</a>
  </p>
</body>
</html>

Enviar test: A tu email primero.

Enviar campaña: Schedule o send now.


Resultados: Open Rate Subió

MailerLite (antes):

  • Open rate: 28% avg
  • Click rate: 6%
  • Deliverability: 95%

Listmonk (después):

  • Open rate: 32% avg (+4%)
  • Click rate: 8% (+2%)
  • Deliverability: 97%

Por qué subió?

1. Plain Text Emails

MailerLite templates = HTML pesado.

Listmonk (mi setup) = plain text con light HTML.

Resultado: ISPs confían más en plain text (menos spam flags).


2. Control Total de Sender

MailerLite = shared IP pool (tu deliverability depende de otros usuarios).

Listmonk self-hosted = tu propio servidor, tu propia reputación.

Setup SMTP: Usé servidor SMTP dedicado (no Gmail, no shared pool).

SPF, DKIM, DMARC: Configurados correctamente.


3. Sin «Marketing Templates»

Templates de MailerLite gritan «NEWSLETTER MARKETING».

Mi template Listmonk = email personal con light formatting.

ISPs detectan: Email de persona real > email de blast marketing.


Automatización: OpenClaw + Listmonk API

Workflow:

  1. Publico blog post en WordPress
  2. OpenClaw detecta post nuevo (webhook)
  3. Extrae title, excerpt, URL
  4. Genera email version (Sonnet)
  5. Crea campaña Listmonk (API)
  6. Schedule para mañana 9 AM

Script Python:

import requests

## Listmonk API credentials
LISTMONK_URL = "https://listmonk.yourdomain.com"
LISTMONK_USER = "username"
LISTMONK_PASS = "password"

## Create campaign
campaign_data = {
    "name": f"Newsletter: {post_title}",
    "subject": generated_subject,
    "body": email_html,
    "lists": [1],  # List ID
    "type": "regular",
    "content_type": "html",
    "send_at": "2026-02-19T09:00:00Z"
}

response = requests.post(
    f"{LISTMONK_URL}/api/campaigns",
    json=campaign_data,
    auth=(LISTMONK_USER, LISTMONK_PASS)
)

campaign_id = response.json()['data']['id']
print(f"Campaign created: {campaign_id}")

Resultado: Blog post → Newsletter automática (zero clicks).


Template Minimalista (Mi Setup)

HTML simple:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
    body {
      font-family: Georgia, serif;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
      color: #333;
    }
    h1 {
      font-size: 24px;
      margin-bottom: 10px;
      color: #000;
    }
    p {
      line-height: 1.6;
      margin-bottom: 15px;
    }
    a {
      color: #0066cc;
      text-decoration: none;
    }
    .cta {
      background: #0066cc;
      color: white;
      padding: 12px 24px;
      display: inline-block;
      margin-top: 20px;
      text-decoration: none;
    }
    .footer {
      color: #666;
      font-size: 14px;
      margin-top: 40px;
      border-top: 1px solid #ddd;
      padding-top: 20px;
    }
  </style>
</head>
<body>
  <h1>{{ .Campaign.Name }}</h1>

  {{ .Campaign.Body }}

  <a href="{{ .Campaign.ArchiveURL }}" class="cta">Read the full post →</a>

  <div class="footer">
    <p>You're receiving this because you signed up at cristiantala.com.</p>
    <p><a href="{{ .UnsubscribeURL }}">Unsubscribe</a></p>
  </div>
</body>
</html>

Por qué funciona:

  • Simple (no bloat)
  • Se ve bien en desktop Y mobile
  • Plain text alternativa (Listmonk la genera auto)
  • Unsubscribe 1-click (compliance)

Lecciones Aprendidas

1. Subject Lines = Humano, No IA

Probé subject lines generados por IA (10 newsletters).

Resultados:

  • AI subjects: 24% open rate
  • Human subjects: 35% open rate

Conclusión: IA puede escribir body, pero subject lines necesitan toque humano.


2. Plain Text > Rich HTML

Test A/B:

  • Rich HTML (imágenes, colores, CTA buttons): 28% open, 15% spam flags
  • Plain text + light formatting: 34% open, 2% spam flags

ISPs prefieren plain text. Readers también.


3. Send Time Matters

Probé enviar a diferentes horas:

  • 6 AM: 18% open (demasiado temprano)
  • 9 AM: 35% open ✅
  • 2 PM: 22% open (inbox lleno)
  • 6 PM: 12% open (modo desconexión)

Sweet spot: 9 AM hora local (lunes-viernes).


4. Frequency Consistency > Frequency High

Antes: Newsletter cada 2 semanas (inconsistente).

Después: Newsletter cada lunes 9 AM (clockwork).

Open rate subió 8% solo con consistencia.

Por qué: Readers esperan el email. Si llega cuando esperan, lo abren.


5. Segmentación ≠ Necesaria (Para Listas Pequeñas)

MailerLite me vendió segmentación avanzada.

Realidad con 1,923 subs: No necesito segmentar.

Contenido es nichado (tech, startups, automation) → todos quieren lo mismo.

Ahorro: No necesito pagar por features que no uso.


Costos: $0 vs $348

MailerLite (1,923 subs):

  • Monthly: $29
  • Annual: $348

Listmonk (self-hosted):

  • VPS: $12/mo (pero corro 8+ servicios, no solo Listmonk)
  • Domain: $12/año (ya lo tengo)
  • SMTP: $0 (uso SMTP del VPS)
  • Total atribuible a newsletter: $0

Setup time:

  • One-time: 2 horas
  • Mantenimiento: 0 horas/mes (Docker auto-update)

Payback: Inmediato (mes 1 ya ahorré $29).


Cuándo NO Self-Hostear

NO self-hostear si:

  • <500 subs (SaaS gratis tiers funcionan)
  • No sabes Docker (learning curve no vale $29/mes)
  • Necesitas features avanzadas (A/B testing automático, ML segmentation)
  • No tienes VPS (comprar VPS solo para newsletter = no vale)

SÍ self-hostear si:

  • 1,000 subs (savings >$20/mes)

  • Ya tienes VPS (costo marginal = $0)
  • Sabes Docker básico (o quieres aprender)
  • Valoras control total (data ownership, deliverability)

Migración Paso a Paso (Checklist)

Pre-Migración

  • [ ] Export subscribers CSV de MailerLite
  • [ ] Backup de templates
  • [ ] Nota de última campaña enviada (para no duplicar)

Setup Listmonk

  • [ ] Levantar Docker (postgres + listmonk)
  • [ ] Configurar Caddy reverse proxy
  • [ ] Verificar SSL funciona
  • [ ] Acceder a Listmonk UI

Configuración

  • [ ] Crear lista principal
  • [ ] Import subscribers CSV
  • [ ] Configurar SMTP (si no usas VPS default)
  • [ ] Setup SPF, DKIM, DMARC records
  • [ ] Crear template email
  • [ ] Test campaña a ti mismo

Primera Campaña

  • [ ] Avisar a subs de migración
  • [ ] Enviar test a 10-20 subs primero
  • [ ] Verificar deliverability
  • [ ] Send to all

Post-Migración

  • [ ] Monitorear opens/clicks primeras 3 campañas
  • [ ] Ajustar template si bounce rate >5%
  • [ ] Cancelar MailerLite (después de 1 mes testing)

Herramientas y Recursos

Listmonk:

  • Repo: https://github.com/knadh/listmonk
  • Docs: https://listmonk.app/docs
  • Docker Hub: https://hub.docker.com/r/listmonk/listmonk

SMTP Testing:

  • Mail Tester: https://www.mail-tester.com (score deliverability)
  • MX Toolbox: https://mxtoolbox.com (DNS checks)

Alternativas a Listmonk:

  • Mautic (más features, más complejo)
  • Sendy (one-time $69, usa Amazon SES)
  • Mailtrain (similar a Listmonk)

Por qué elegí Listmonk:

  • Más simple (Mautic es overkill)
  • Gratis (Sendy = $69)
  • Activamente mantenido
  • Docker-ready
  • API completa

Conclusión: $348/Año Saved, Control Total

Setup time: 2 horas (one-time).

Ahorro annual: $348.

ROI: $174/hora de setup work.

Bonus:

  • Open rate subió 4%
  • Control total deliverability
  • Data ownership (no vendor lock-in)
  • Aprende Docker (skill transferible)

Cuándo self-hostear:

  • 1,000 subs

  • Ya tienes VPS
  • Valoras control > convenience

Cuándo NO:

  • <500 subs
  • No sabes Docker (y no quieres aprender)
  • Pagas por features avanzadas SaaS

Si envías newsletters y tienes >1,000 subs, migrar a Listmonk paga su peso en oro.

2 horas de setup. $348/año saved. Para siempre.


Repo con configs completos:


¿Usas newsletter self-hosted? ¿Qué plataforma? Comparte en comentarios.

Suscríbete a mi Newsletter

Estrategias de automatización, AI y startups que funcionan. Análisis semanal directo a tu inbox. (Sin spam, prometo intentarlo.)