Mailcow en un VPS: correo propio que Gmail acepta
Comprueba el puerto 25 antes de instalar mailcow: un bloqueo deja el correo en cola hasta rebotar. Configura DNS para evitar el rechazo 550 5.7.25 de Gmail por PTR.
Qué va a implementar
Un servidor de correo completo en un equipo bajo su control: SMTP para enviar y recibir, IMAP para mantener sincronizados el teléfono y el portátil, un cliente de webmail y un filtro antispam que puntúa todos los mensajes en ambas direcciones. mailcow-dockerized agrupa Postfix, Dovecot, Rspamd, el webmail SOGo, MariaDB, Redis y un cliente ACME en una sola pila de Docker Compose, por lo que el software no es la parte complicada. Lo tendrá funcionando en media hora.
La parte complicada es todo lo que lo rodea. El correo electrónico es el único servicio en el que el resto de Internet desconfía activamente de un servidor recién instalado, y la diferencia entre «funciona» y «Gmail descarta todos los mensajes sin avisar» depende sobre todo de cuatro registros DNS y de un ajuste de reputación de IP que quizá no pueda controlar por completo. Lea los requisitos previos antes de alquilar nada. Si después de leerlos decide que el esfuerzo necesario para conseguir una buena reputación no compensa, es una respuesta válida: nuestro resumen de lo que realmente merece la pena alojar por cuenta propia en 2026 clasifica el correo electrónico como «solo si se lo toma en serio» precisamente por estos motivos.
Los prerrequisitos SON el proyecto
Si omite cualquiera de estos puntos, enviará correo que nunca llegará. Este es el orden aproximado según la frecuencia con la que cada punto causa problemas:
El puerto saliente 25 debe estar abierto. El servidor entrega correo a Gmail y Microsoft mediante TCP en el puerto 25. Muchos proveedores de VPS y nube bloquean por defecto el puerto 25 saliente para combatir el spam. El bloqueo es silencioso: no aparece ningún error durante el arranque, todo parece funcionar y el correo permanece aplazado en la cola hasta que Postfix abandona el intento y lo devuelve. mailcow establece en cinco días la duración máxima de la cola. Compruébelo antes de instalar nada. Si está bloqueado, la única solución es abrir un ticket de soporte y pedir al proveedor que lo habilite. Algunos lo hacen para cuentas con cierta antigüedad; otros nunca lo habilitan.
Una IP limpia y con una reputación utilizable. Las IP recicladas de VPS suelen aparecer ya en listas de bloqueo debido al spam del inquilino anterior. Compruebe la suya en un servicio como la consulta de Spamhaus o mxtoolbox antes de comprometerse. Una IP incluida en una lista implica rechazos que no podrá resolver mediante configuración o código.
Control del DNS y un registro PTR correcto. Necesita añadir registros a la zona de su dominio y configurar el DNS inverso (PTR) de la IP del servidor para que apunte al nombre de host de correo. El PTR casi nunca se configura en el panel de DNS. Lo gestiona quien controla la IP, por lo que debe establecerlo en el panel de control del proveedor de VPS o mediante un ticket.
6 GiB de RAM y 2 vCPU son el mínimo cómodo. El mínimo indicado por mailcow es 6 GiB de RAM más 1 GiB de swap para una instalación privada. Se recomiendan 8 GiB cuando varios usuarios dependen del servicio. Por debajo de unos 2.5 GiB, generate_config.sh ofrece desactivar el antivirus ClamAV para evitar que el kernel empiece a finalizar contenedores. El mínimo de disco documentado por mailcow es 20 GiB antes de almacenar cualquier correo. Por tanto, asigne más espacio desde el principio y deje margen para que crezcan los buzones.
Un nombre DNS, no una IP sin nombre. Elija un nombre de host como mail.example.com. Ese único nombre será su MAILCOW_HOSTNAME, el sujeto del certificado TLS, el destino del PTR y el banner SMTP. Manténgalo coherente en todos los lugares.
Paso 1: Compruebe que el puerto saliente 25 esté abierto
Haga esto primero. Si falla, todo lo demás es trabajo perdido. Desde el VPS recién creado, intente abrir una conversación SMTP con un servidor de correo real:
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 gmail-smtp-in.l.google.com 25Si funciona, el resultado es inmediato:
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!Un puerto bloqueado espera durante los cinco segundos completos y después falla:
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progressEn un VPS recién creado sin reglas de firewall salientes propias, ese tiempo de espera indica un bloqueo del proveedor. Es un filtro de red del proveedor, no de su firewall, por lo que ningún cambio local lo solucionará. Abra un ticket: "Please enable outbound TCP port 25 for my VPS at <IP>; I am running a legitimate mail server." No instale mailcow hasta que este comando devuelva "succeeded". Tenga en cuenta que el puerto 25 entrante, utilizado por otros servidores para conectarse a usted, sigue una ruta independiente y normalmente está abierto. El proveedor suele limitar el tráfico saliente.
Paso 2: configure ahora los registros DNS
Los cambios de DNS tardan en propagarse, así que publique todo lo que pueda antes de instalar. Suponga que su dominio es example.com, que su host de correo es mail.example.com y que la IP es 10.0.0.10. Cree lo siguiente en su zona:
mail.example.com. A 10.0.0.10
mail.example.com. AAAA 2001:db8::10 ; only if you have IPv6
example.com. MX 10 mail.example.com.
example.com. TXT "v=spf1 mx -all"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:postmaster@example.com"El registro SPF indica que «solo mi MX puede enviar correo para este dominio; rechace el resto». Empiece DMARC con p=none para poder supervisar los informes sin rechazar sus propios mensajes. Cambie después a p=quarantine y, finalmente, a p=reject cuando haya confirmado la alineación. Faltan dos registros de forma intencionada: DKIM, que mailcow genera en el Paso 6, y PTR, que debe configurar ahora en el panel de su proveedor.
Configure el PTR (DNS inverso) de 10.0.0.10 con el valor mail.example.com, exactamente MAILCOW_HOSTNAME. Este es el registro que más se suele olvidar, y los proveedores grandes rechazan mensajes cuando falta o es incorrecto. Si el panel no tiene un campo para rDNS, abra un ticket.
Paso 3: instalar Docker
mailcow necesita Docker Engine 24.0 o una versión posterior y Docker Compose 2.0 o una versión posterior. El plugin de Compose (docker compose) es la opción más sencilla, aunque mailcow también admite el binario independiente docker-compose. Use el script oficial de conveniencia de Docker en lugar del paquete docker.io de Ubuntu, que no incluye ningún plugin de Compose:
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose versionDebería ver una línea Docker Compose version que indique v2.x o v5.x. Compose v5, publicado en 2025, es funcionalmente idéntico a v2 y cualquiera de las dos versiones cumple los requisitos de mailcow. Si docker compose version muestra docker: 'compose' is not a docker command, Docker Engine está instalado, pero falta el plugin de Compose. Instale el plugin desde el repositorio de Docker, vuelva a ejecutar el script anterior o consulte nuestra guía básica de Docker Compose, que configura ambos componentes desde el repositorio apt de Docker.
Paso 4: Clonar mailcow y generar la configuración
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.shComprobar que umask muestra 0022 sigue los pasos de instalación propios de mailcow, que establecen umask 0022 antes de clonar el repositorio. mailcow no comprueba ese valor, y una shell nueva de root en Ubuntu 24.04 ya proporciona 0022. Después, el script solicita el único dato importante: el nombre de host completo. Introduzca mail.example.com. El valor debe coincidir exactamente con los registros A y PTR. El script escribe mailcow.conf, el único archivo de entorno que lee toda la pila. Ábralo si necesita cambiar los puertos web (HTTP_PORT, HTTPS_PORT) o desactivar ClamAV en un equipo con pocos recursos:
MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=80
HTTPS_PORT=443
SKIP_CLAMD=n # set to y to drop the virus scanner on a <2.5 GiB boxSKIP_FTS=y es el otro ajuste importante en un equipo con poca RAM: la búsqueda de texto completo es el segundo consumidor de memoria que indica la documentación de mailcow, y omitirla sólo elimina la búsqueda del contenido de los mensajes en el webmail.
Mantenga HTTP_PORT=80 y HTTPS_PORT=443 salvo que otro servicio del host ya los utilice. El cliente ACME integrado de mailcow usa el desafío HTTP-01 de forma predeterminada, por lo que necesita que el puerto 80 sea accesible desde Internet para obtener el certificado. (mailcow 2026-03 y versiones posteriores también pueden usar un desafío DNS-01 con ACME_DNS_CHALLENGE=y. Esta opción no necesita el puerto 80, pero sí acceso a la API de su proveedor DNS.) Por este motivo, el diseño sencillo no ejecuta una configuración independiente de nginx y Certbot en el mismo equipo. mailcow emite y renueva sus propios certificados TLS internamente, y un segundo servicio que ocupe 80/443 provoca conflictos. mailcow documenta la alternativa: vincular localmente sus listeners web con HTTP_BIND=127.0.0.1 HTTP_PORT=8080 HTTPS_BIND=127.0.0.1 HTTPS_PORT=8443, colocar un reverse proxy en 80 y 443 y, si quiere que un Certbot local administre los certificados, indicar a mailcow dónde encontrarlos y añadir un hook de renovación. Esto añade más componentes en un servidor de correo, por lo que se aplica el mismo criterio a cualquier otro servicio que necesite una interfaz web pública: una instalación como Halcyon transformando una biblioteca de Jellyfin en un videoclub de los años 90, que espera su propio reverse proxy en 443, debe ejecutarse en otro host salvo que coloque mailcow detrás de ese proxy. La excepción es un servicio que no solicite ningún puerto web público: un relay de RustDesk autohospedado sólo escucha en su propio rango de 21115 a 21119, por lo que puede compartir el equipo si dispone de suficiente ancho de banda.
Paso 5, Iniciar la pila e iniciar sesión
sudo docker compose pull
sudo docker compose up -d
sudo docker compose psLa descarga obtiene aproximadamente dos docenas de imágenes; espere unos minutos. Cuando docker compose ps muestre que todos los contenedores están running (o healthy), abra https://mail.example.com/admin en un navegador. Las credenciales de administrador predeterminadas son el nombre de usuario admin y la contraseña moohoo. Cambie esa contraseña inmediatamente en la interfaz de administración, en System → Configuration y después en Access → Administrators. Si el navegador muestra la advertencia NET::ERR_CERT_AUTHORITY_INVALID, todavía no se ha emitido el certificado ACME. Consulte el apartado sobre errores de ACME antes de asumir que hay un problema; es normal que aparezca un certificado autofirmado durante el primer minuto o los dos primeros minutos.
Paso 6, Añadir un dominio, un buzón y publicar DKIM
En la interfaz de administración, abra E-Mail → Configuration y, en la pestaña Domains, haga clic en Add domain e introduzca example.com. Después, en Mailboxes, haga clic en Add mailbox para crear you@example.com con una contraseña. Ese buzón ya funciona y es accesible mediante IMAP.
Ahora, la clave DKIM. Vaya a System → Configuration y después a Options → ARC/DKIM keys. Es posible que mailcow ya haya generado una clave al añadir el dominio. Si no lo ha hecho, genérela allí, seleccione el dominio, mantenga el selector dkim, elija 2048-bit y haga clic en Add. Copie el valor TXT largo que se muestra y publíquelo como:
dkim._domainkey.example.com. TXT "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"La página Domains de mailcow tiene un botón DNS que muestra todos los registros esperados y coloca una marca verde o una cruz roja según lo que esté publicado realmente. Úselo como lista de comprobación y consiga que todas las filas aparezcan en verde antes de probar la entregabilidad. Una fila DKIM roja después de publicar el registro suele indicar que la clave se dividió incorrectamente entre varios fragmentos TXT. Una clave de 2048 bits supera el límite de 255 caracteres de una única cadena TXT, por lo que debe pegarla como un único valor lógico y permitir que el proveedor de DNS la divida en fragmentos.
Paso 7. Compruebe la entregabilidad y llegue a 10/10
Vaya a mail-tester.com, copie la dirección aleatoria que muestra y envíele un mensaje desde su nuevo buzón. Inicie sesión en el webmail de SOGo en https://mail.example.com/SOGo y envíelo desde allí. Después, haga clic en «Then check your score».
El objetivo es obtener 10/10. Estas son las deducciones más habituales y sus causas:
- SPF no alineado: falta el registro
MX/SPF o la IP de envío no está incluida. Vuelva a comprobar el registro SPF TXT. - La firma DKIM no se puede verificar: falta el registro TXT
dkim._domainkey, todavía no se ha propagado o está dañado. Es el error más habitual. - No hay PTR o el PTR no coincide: el DNS inverso no resuelve a
mail.example.com. Corríjalo con el proveedor. - La IP aparece en una lista de bloqueo: se debe a la reputación anterior de la IP. Solicite su eliminación de la lista o pida una IP con mejor reputación.
No envíe mensajes reales a Gmail ni a Outlook hasta obtener 10/10. Una puntuación baja combinada con una IP nueva puede hacer que su dominio quede marcado desde el primer día.
Paso 8: Conecte un cliente de correo real
Configure Thunderbird, Apple Mail o su teléfono para conectarse al servidor con estos ajustes. El host del servidor es mail.example.com en todos los casos:
- IMAP: puerto 993, SSL/TLS (o 143 con STARTTLS)
- Envío SMTP: puerto 465, SSL/TLS (o 587 con STARTTLS)
- Nombre de usuario: la dirección completa,
you@example.com - Contraseña: la contraseña del buzón que configuró
No envíe nunca correo del cliente por el puerto 25. Ese puerto sólo se usa entre servidores. mailcow no ofrece envío autenticado en ese puerto y rechaza los clientes que intentan usarlo. Si un cliente muestra Relay access denied, intenta enviar por el puerto 25 o sin autenticación. Cambie la configuración al puerto 465 o 587 y use las credenciales del buzón.
Paso 9: haga copias de seguridad de lo que realmente importa
mailcow incluye un script de copia de seguridad que crea una instantánea de cada volumen con estado. Ejecútelo en un disco externo o en un recurso remoto montado:
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall captura seis elementos. Si pierde cualquiera de ellos, pierde datos: vmail (los buzones reales), crypt (las claves que descifran vmail; sin ellas no sirven), mysql (la instancia de MariaDB que almacena dominios, usuarios, alias y configuración), redis (el estado de las colas y la caché), rspamd (los datos aprendidos sobre spam y correo legítimo) y postfix (la cola de correo). Se ejecuta dentro de un contenedor auxiliar que escribe archivos comprimidos, por lo que las copias mantienen la coherencia aunque la pila esté activa. Automatícelo con una tarea cron nocturna y añada --delete-days 14 para eliminar los conjuntos antiguos. Una tarea cron que deja de funcionar falla de forma silenciosa, así que envíe el estado de salida del script a un lugar donde realmente lo vea; un servidor ntfy autohospedado acepta un comando curl de una línea desde la misma entrada de cron y envía el fallo al teléfono la noche en que ocurre. La restauración usa el mismo script con restore. Este comando muestra las instantáneas y permite elegir qué restaurar. Una copia de seguridad que nunca se ha probado mediante una restauración es una esperanza, no una copia de seguridad. Haga una prueba en seco en una VPS desechable.
Paso 10, Actualice según un calendario
mailcow se actualiza mediante su propio script. Este descarga código nuevo, migra mailcow.conf, precarga imágenes y reinicia los contenedores en orden:
cd /opt/mailcow-dockerized
sudo ./update.sh --check # reports whether an update exists, changes nothing
sudo ./update.sh # applies itHaga primero una copia de seguridad (paso 9), porque una migración del esquema es difícil de revertir. Las actualizaciones se publican con frecuencia e incluyen correcciones de seguridad para daemons expuestos a Internet, por lo que no debe dejar un servidor de correo sin actualizar durante meses. Si una actualización deja algún contenedor en estado no saludable, sudo docker compose logs --tail=50 <service>-mailcow indica qué daemon no volvió a iniciarse.
Una nota sobre el refuerzo de la seguridad
mailcow ejecuta su propio servicio netfilter (netfilter-mailcow), que bloquea las IP que realizan numerosos intentos contra los puertos de correo y webmail. Por eso, la parte de correo queda protegida de forma predeterminada. Esto no protege SSH en el propio host, que sigue expuesto y recibe ataques de fuerza bruta. Combine esta configuración con Fail2ban supervisando el registro de autenticación de SSH y permita el inicio de sesión sólo con claves. Proteja la interfaz de administración de mailcow con una contraseña segura y, preferiblemente, no la exponga a Internet pública o colóquela detrás de una VPN.
Modos de fallo, con las cadenas exactas
Las colas de correo se llenan y nunca se entrega ningún mensaje. Ejecute sudo docker compose exec postfix-mailcow postqueue -p o consulte la cola de correo en la interfaz de administración. Los mensajes permanecen aplazados hasta cinco días antes de que Postfix los rechace, con:
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)Casi siempre se debe a que su proveedor bloquea el puerto saliente 25 (Paso 1). Una regla saliente en el firewall del propio host produce el mismo tiempo de espera, así que descártelo primero. Si el host no tiene problemas, ninguna modificación de la configuración lo resolverá: abra un ticket. No es un problema de DNS ni de TLS. La indicación es la palabra timed out frente a un MX remoto en el puerto 25.
Gmail marca todos los mensajes como spam o los rechaza. Abra el mensaje en Gmail, seleccione "Show original" y lea los resultados de autenticación. dkim=fail o dkim=none significa que el registro TXT de dkim._domainkey falta, está alterado o aún no se ha propagado. Vuelva a publicar exactamente lo que muestra la página de ARC/DKIM y espere a que transcurra el TTL. spf=fail significa que los registros SPF/MX no incluyen su IP. La alineación es fundamental: basta con una comprobación fallida para que el mensaje termine en spam.
Los grandes proveedores rechazan la conexión. Los rebotes o los registros de Postfix contienen el rechazo de PTR de Gmail:
550-5.7.25 [10.0.0.10] The IP address sending this message does not have a PTR
550-5.7.25 record setup, or the corresponding forward DNS entry does not match
550 5.7.25 the sending IP. As a policy, Gmail does not accept messages from IPs
550 5.7.25 with missing PTR records.El código 550 5.7.25 significa que falta el DNS inverso o que no coincide. Configure mail.example.com como PTR de su IP en el proveedor (Paso 2). Los registros directo (A) e inverso (PTR) deben coincidir y ambos deben identificar el mismo host con el que mailcow se presenta ante otros servidores.
El navegador muestra una advertencia de certificado que no desaparece. El contenedor acme-mailcow no pudo obtener un certificado válido. Revise su registro:
sudo docker compose logs acme-mailcow | tail -n 40Una línea como Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. o un fallo del desafío significa que el puerto 80 no es accesible desde Internet o que el registro A no apunta a este servidor. Si publicó un registro AAAA, compruébelo también: mailcow valida primero el registro AAAA y omite la comprobación de A cuando existe uno. Por tanto, un AAAA incorrecto o una ruta IPv6 que no pueda alcanzar el puerto 80 hace que la validación falle aunque el registro A sea correcto. Confirme que mail.example.com resuelve a este equipo, permita los puertos 80 y 443 en cualquier firewall del host y asegúrese de que ningún otro proceso los esté usando. Después de corregir la causa, reinicie el cliente con sudo docker compose restart acme-mailcow en lugar de esperar a que termine el retroceso de una hora.
FAQ
¿Realmente merece la pena alojar el correo por cuenta propia?
Si quiere controlar los datos, disponer de alias ilimitados y tener control total, sí: mailcow ofrece una pila profesional por el precio de un VPS. Pero la entregabilidad requiere atención continua: la reputación de la IP, la alineación de DNS y la supervisión de listas de bloqueo nunca dejan de ser necesarias. Para una dirección empresarial crítica, en la que perder un solo día en la carpeta de spam de alguien tiene un coste, un proveedor gestionado es la opción más práctica. Aloje el correo por cuenta propia si valora más el control que la comodidad y realmente va a administrarlo. Si lo que quiere es que sus datos no estén en los servidores de otra persona, y no necesariamente gestionar el correo, empiece por un servicio en el que nadie más pueda decidir: una biblioteca de fotos no tiene ningún problema de entregabilidad, y nuestra comparativa de PhotoPrism e Immich incluye los requisitos mínimos de RAM y los comandos de copia de seguridad para desplegarla en el mismo tipo de VPS.
¿Cómo sé si el puerto saliente 25 está bloqueado?
Ejecute nc -vz -w 5 gmail-smtp-in.l.google.com 25 desde el servidor. "succeeded!" significa que está abierto; un timed out después de la espera significa que el puerto está bloqueado. En un equipo sin reglas de firewall salientes propias, el bloqueo procede del proveedor. Esta es la causa más habitual de que un servidor autogestionado pueda recibir correo, pero nunca enviarlo. La única solución para un bloqueo del proveedor es que el proveedor abra el puerto. Ningún cambio de configuración local puede resolverlo.
¿Por qué mi correo sigue llegando a la carpeta de spam de Gmail?
Casi siempre se debe a una cadena de autenticación incorrecta. Use "Show original" en Gmail y busque spf=pass, dkim=pass y dmarc=pass. Un dkim=fail indica que falta el registro TXT dkim._domainkey o que está dañado. Una discrepancia en el PTR o una IP nueva sin historial de envío también perjudican la entregabilidad. Consiga primero una puntuación de 10/10 en mail-tester.com. Después, caliente la IP poco a poco: envíe unos pocos mensajes al día y aumente el volumen gradualmente, en lugar de enviar muchos mensajes desde el primer día.
¿Qué necesito incluir exactamente en la copia de seguridad?
Ejecute backup_and_restore.sh backup all y conserve el conjunto completo fuera del servidor. Incluye vmail (los buzones), crypt (las claves que los descifran), la base de datos MariaDB (dominios, usuarios, alias y configuración), Redis, los datos aprendidos por Rspamd y la cola de Postfix. El volumen crypt suele pasarse por alto. Sin él, la copia de seguridad de vmail contiene texto cifrado ilegible. Pruebe a restaurarla al menos una vez en un equipo temporal.
¿Puedo ejecutar mailcow en un VPS de 2 GB?
No de forma estable. generate_config.sh ofrece deshabilitar ClamAV por debajo de unos 2.5 GiB. Incluso con ClamAV deshabilitado, Rspamd, Dovecot, MariaDB y el resto de la pila compiten por la memoria. Por tanto, se producirán usos intensivos de swap y terminaciones por OOM con cualquier carga real. Considere 6 GiB más 1 GiB de swap como el mínimo para una instalación estable de un solo usuario. Pase a 8 GiB en cuanto dependan del servicio más de un par de personas.