Mailcow en un VPS: correo propio que Gmail acepta
Compruebe el puerto 25 antes de instalar mailcow: un bloqueo deja el correo en cola. Configure el 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 que usted controla: 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 asigna una puntuación a cada mensaje 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 difícil. Lo tendrá funcionando en media hora.
La parte difícil 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 nuevo. La diferencia entre «funciona» y «Gmail elimina todos los mensajes sin avisar» depende 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 no compensa trabajar para mejorar la reputación, es una respuesta legítima: nuestro resumen de lo que realmente merece la pena alojar por cuenta propia en 2026 clasifica el correo electrónico como «sólo si se lo toma en serio» exactamente por estos motivos.
Los requisitos previos SON el proyecto
Si omite uno solo de estos requisitos, 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. Su servidor entrega correo a Gmail y Microsoft mediante TCP en el puerto 25. Muchos proveedores de VPS y servicios en la nube bloquean por defecto las conexiones salientes al puerto 25 para combatir el spam. El bloqueo es silencioso: no aparece ningún error durante el arranque, todo parece funcionar y el correo simplemente permanece en la cola indefinidamente. 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á solucionar 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 del correo. El PTR casi nunca se configura en el panel de DNS. Lo administra quien controla la IP, por lo que debe configurarlo en el panel de control del proveedor de VPS o mediante un ticket.
6 GiB de RAM y 2 vCPU son el mínimo razonable. 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 aproximadamente 2.5 GiB, generate_config.sh ofrece deshabilitar el antivirus ClamAV para evitar que el kernel empiece a terminar contenedores. Reserve 20 GB de SSD para empezar.
Un nombre DNS, no una IP sin nombre. Elija un nombre de host como mail.example.com. Ese único nombre se convierte en su MAILCOW_HOSTNAME, en el sujeto del certificado TLS, en el destino del PTR y en el banner SMTP. Manténgalo coherente en todas partes.
Paso 1: Compruebe que el puerto saliente 25 está abierto
Haga esto primero. Todo lo demás es trabajo perdido si esta prueba falla. 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 25Un resultado correcto es inmediato:
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!Un puerto bloqueado espera 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 progressEse tiempo de espera indica el bloqueo. Es un filtro de red aplicado por el proveedor, no por su firewall, por lo que ningún cambio local lo soluciona. Abra un ticket con este texto: "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 entrante 25 (el acceso de otros servidores a su servidor) sigue una ruta independiente y normalmente está abierto. Lo que los proveedores limitan es 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 con el plugin Compose v2. Use el script oficial 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 v2.x. Si docker compose version muestra docker: 'compose' is not a docker command, Docker Engine está instalado, pero el plugin de Compose no. Instale el plugin desde el repositorio de Docker, vuelva a ejecutar el script anterior o siga nuestra guía básica de Docker Compose, que configura ambos componentes desde el propio 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.shCompruebe que umask muestre primero 0022. mailcow no puede compilarse con una máscara de archivos impar, y un shell de root nuevo en Ubuntu 24.04 ya establece 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 deshabilitar ClamAV en un equipo pequeño:
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 menciona la documentación de mailcow, y omitirla sólo impide buscar en el texto del cuerpo de los mensajes desde el webmail.
Deje HTTP_PORT=80 y HTTPS_PORT=443 sin cambios, a menos que otro servicio del host ya los utilice. El cliente ACME integrado de mailcow necesita que el puerto 80 sea accesible desde Internet para obtener el certificado. Por eso no debe ejecutar una configuración independiente de nginx y Certbot en el mismo equipo: mailcow emite y renueva sus propios certificados TLS internamente, y otro servicio ocupando 80/443 provoca conflictos. Lo mismo se aplica a cualquier otro servicio que necesite una interfaz web pública. Por tanto, una configuración como la modificación de Halcyon que convierte 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. La excepción es un servicio que nunca solicite un puerto web público: un relay de RustDesk autoalojado 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, Inicie la pila e inicie 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 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 Access → Administrators. Si el navegador muestra la advertencia NET::ERR_CERT_AUTHORITY_INVALID, el certificado ACME todavía no se ha emitido. Consulte el apartado sobre errores de ACME antes de asumir que hay un problema; un certificado autofirmado temporal es normal durante los primeros uno o dos minutos.
Paso 6: Añadir un dominio, un buzón y publicar DKIM
En la interfaz de administración, abra la página Mail Setup (Configuration → Mail Setup). 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. El buzón ya estará operativo y será accesible mediante IMAP.
Ahora configure la clave DKIM. Vaya a Configuration → ARC/DKIM keys. Es posible que mailcow ya haya generado una clave al añadir el dominio. Si no es así, genérela allí, seleccione el dominio, mantenga el selector dkim, elija 2048-bit y haga clic en Add. Copie el valor TXT completo 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 una marca verde o una cruz roja según lo que esté publicado. Úselo como lista de comprobación y consiga que todas las filas aparezcan en verde antes de probar la entregabilidad. Una fila DKIM en rojo después de publicar el registro suele indicar que la clave se dividió incorrectamente entre varios fragmentos TXT. Una clave de 2048-bit supera el límite de 255 caracteres de una única cadena TXT. Péguela como un único valor lógico y deje que el proveedor 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 y cadenas exactas
Las colas de correo se llenan y los mensajes nunca se entregan. Ejecute sudo docker compose exec postfix-mailcow postqueue -p o consulte la cola de correo en la interfaz de administración. Las entradas permanecen aplazadas con:
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)El proveedor está bloqueando el puerto saliente 25 (paso 1). Ningún cambio de configuración lo solucionará. Abra un ticket. No es un problema de DNS ni de TLS. La indicación es la palabra timed out junto a un MX remoto en el puerto 25.
Gmail marca todo como spam o rechaza los mensajes. Abra el mensaje en Gmail, seleccione "Mostrar original" y revise los resultados de autenticación. dkim=fail o dkim=none significa que el registro TXT de dkim._domainkey falta, está dañado 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. Un solo control fallido basta para que el mensaje termine en spam.
Los proveedores grandes 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 indicar el mismo host con el que mailcow se presenta ante otros servidores.
El navegador muestra una advertencia de certificado que nunca 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. Confirme que mail.example.com resuelve a este equipo, abra los puertos 80 y 443 en cualquier firewall del host y asegúrese de que ningún otro proceso esté enlazado a esos puertos. Después de corregir la causa, reinicie el cliente con sudo docker compose restart acme-mailcow en lugar de esperar a que termine la espera de una hora.
FAQ
¿Realmente vale la pena alojar el correo por cuenta propia?
Si quiere controlar sus datos, tener alias ilimitados y disponer de control total, sí. mailcow ofrece una pila profesional por el precio de un VPS. Sin embargo, la entregabilidad requiere atención constante: la reputación de la IP, la alineación de DNS y la supervisión de listas de bloqueo nunca terminan del todo. Para una dirección empresarial crítica en la que un solo día en la carpeta de spam de alguien le ocasione pérdidas, un proveedor gestionado es la opción 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 opinar: una biblioteca de fotos no tiene ningún problema de entregabilidad, y nuestra comparación de PhotoPrism e Immich incluye los requisitos mínimos de RAM y los comandos de copia de seguridad para ese despliegue en un VPS similar.
¿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 su proveedor lo bloquea. Esta es, con diferencia, la causa más habitual de que un servidor autogestionado pueda recibir correo, pero nunca enviarlo. La única solución es que el proveedor abra el puerto. Ningún cambio de configuración local puede desbloquearlo.
¿Por qué mi correo sigue llegando a la carpeta de spam de Gmail?
Casi siempre se debe a una cadena de autenticación rota. 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 su contenido es incorrecto. Una discrepancia en el PTR o una IP nueva sin historial de envío también perjudican la entregabilidad. Primero consiga 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 gradualmente el volumen, 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 todo el conjunto 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 es el elemento que más se suele pasar por alto. Sin él, la copia de seguridad de vmail contiene texto cifrado ilegible. Pruebe al menos una vez la restauración en un equipo desechable.
¿Puedo ejecutar mailcow en un VPS de 2 GB?
No de forma cómoda. generate_config.sh ofrece desactivar ClamAV por debajo de aproximadamente 2.5 GiB. Incluso así, Rspamd, ClamAV, Dovecot y MariaDB compiten por la memoria, por lo que cualquier carga real provocará uso de swap y terminaciones por OOM. 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.