Cloud AWS

VPC en AWS con EC2 y RDS: guía práctica paso a paso

Monté desde cero una VPC en AWS con subredes públicas y privadas, EC2 y RDS. Estos son los pasos reales y los errores que me encontré en el camino.

11 min
AWS VPC Cloud DevOps Networking

Tener el diagrama de una VPC clara en la cabeza es una cosa. Llevarlo a la consola de AWS y que todo hable entre sí sin que un Security Group mal ubicado te tire un error críptico, es otra completamente distinta. En este artículo documento paso a paso cómo levanté una arquitectura clásica de dos capas en AWS —EC2 en una subred pública, RDS en una subred privada— con capturas reales de cada etapa, y sin esconder los errores que fui encontrando en el camino, porque son justo esos errores los que más cuestan encontrar en la documentación oficial.

Si estás empezando con AWS y quieres pasar de la teoría de redes a algo desplegado y funcionando de verdad, esta guía es ese camino recorrido.

La arquitectura que vamos a construir

El objetivo es el patrón más recomendado por AWS para una aplicación web tradicional: un servidor web accesible desde internet, y una base de datos completamente aislada que solo ese servidor puede alcanzar.

Diagrama de la arquitectura VPC con subred pública, subred privada, EC2, RDS, Internet Gateway y NAT Gateway Diagrama de referencia: VPC 10.0.0.0/16, subred pública 10.0.1.0/24 con la EC2, subred privada 10.0.2.0/24 con la RDS.

Los componentes:

  • VPC: 10.0.0.0/16, el contenedor de red completo.
  • Subred pública: 10.0.1.0/24, con salida directa a internet vía Internet Gateway. Aquí vive la EC2.
  • Subred privada: 10.0.2.0/24, sin acceso directo desde internet. Aquí vive la RDS.
  • Internet Gateway (IGW): la puerta de entrada/salida para la subred pública.
  • NAT Gateway: permite que la subred privada salga a internet (para actualizaciones, por ejemplo) sin que nadie desde afuera pueda entrar.
  • Security Groups: el firewall a nivel de instancia para la EC2 y para la RDS.

Paso 1: crear la VPC y las subredes

Lo primero es el terreno. En la consola de AWS, dentro de VPC, el asistente “VPC and more” (“VPC y más”) permite crear en un solo flujo la VPC, las subredes, el Internet Gateway y las tablas de ruteo básicas, evitando errores manuales de configuración.

Asistente de creación de VPC en la consola de AWS Creación de la VPC con el bloque CIDR 10.0.0.0/16.

Subredes pública y privada creadas dentro de la VPC Subredes pública y privada creadas dentro de la VPC Subredes pública y privada creadas dentro de la VPC Subred pública 10.0.1.0/24 y subred privada 10.0.2.0/24, ambas en la misma zona de disponibilidad.

Nota importante: ambas subredes deben crearse en la misma zona de disponibilidad (AZ) para replicar exactamente el diagrama inicial — aunque, como vas a ver más adelante, esto va a generar un problema específico cuando lleguemos a RDS.

Paso 2: Internet Gateway y tabla de ruteo pública

El Internet Gateway (IGW) es lo que le da a la subred pública una salida real al mundo exterior. Una vez creado y adjuntado a la VPC, hay que decirle a la subred pública que lo use:

  • Destino: 0.0.0.0/0
  • Target: el Internet Gateway (igw-xxxxxxxx)

Tabla de ruteo pública con la ruta hacia el Internet Gateway Tabla de ruteo pública con la ruta hacia el Internet Gateway Tabla de ruteo pública con la ruta hacia el Internet Gateway Tabla de ruteo pública con la ruta hacia el Internet Gateway Tabla de ruteo pública asociada a la subred 10.0.1.0/24, con la ruta 0.0.0.0/0 → IGW.

Sin esta ruta explícita, no importa que llames “pública” a la subred: técnicamente seguirá aislada.

Paso 3: NAT Gateway para la subred privada

Este es el paso donde más dudas suelen aparecer, porque el NAT Gateway le da salida a la subred privada, pero físicamente vive en la subred pública.

Formulario de creación del NAT Gateway en la consola de AWS Configuración del NAT Gateway: subred pública, tipo de conectividad “Public”, e IP elástica asignada.

Los campos clave al crearlo:

CampoValorPor qué
SubnetLa subred públicaEl NAT necesita hablar directo con el IGW
Connectivity typePublicVa a comunicarse con internet público
Elastic IPAsignar una nuevaLe da al NAT una IP fija para “dar la cara” hacia afuera
Primary private IPv4 addressEn blancoAWS asigna una IP privada interna automáticamente; lo único que importa es la IP elástica pública

Después de crear el NAT Gateway, el paso que es fácil olvidar: editar la tabla de ruteo privada y agregar la ruta 0.0.0.0/0 apuntando al NAT Gateway recién creado. Sin este paso, el NAT existe pero nadie lo está usando.

Tabla de ruteo privada con la ruta hacia el NAT Gateway Tabla de ruteo privada con la ruta hacia el NAT Gateway Tabla de ruteo privada con 0.0.0.0/0 apuntando al NAT Gateway.

Paso 4: Security Groups, el firewall de cada capa

Antes de lanzar cualquier instancia, se definen sus reglas de acceso:

  • Security Group de la EC2: permite entrante en el puerto 80/443 (HTTP/HTTPS) desde 0.0.0.0/0.
  • Security Group de la RDS: permite entrante en el puerto 5432 (PostgreSQL) únicamente con origen el Security Group de la EC2 — nunca abierto a internet.

Reglas de entrada del Security Group de la EC2 y de la RDS Reglas de entrada del Security Group de la EC2 y de la RDS Security Group de la EC2 (izquierda) y de la RDS (derecha), con el segundo restringido al primero como origen.

Paso 5: lanzar la EC2 en la subred pública

Al lanzar la instancia, dos detalles no son opcionales:

  • Elegir la subred pública.
  • Habilitar “Asignar automáticamente IP pública” — sin esto, la instancia queda en la subred correcta pero sigue sin IP visible desde internet.

Configuración de red al lanzar la instancia EC2 EC2 lanzada en la subred pública, con IP pública automática habilitada y el Security Group del paso anterior asociado.

Paso 6: el primer error real — RDS y EC2 “en VPCs distintas”

Al intentar crear la base de datos PostgreSQL, este fue el error exacto que me detuvo:

The DB instance and EC2 security group are in different VPCs.
The DB instance is in vpc-0f7d0fb137341da72 and the EC2 security
group is in vpc-09175c2e6f13c712f

Mensaje de error al crear la instancia RDS por incompatibilidad de VPC El error de AWS al intentar asociar un Security Group que vive en una VPC distinta a la de la RDS.

La causa raíz: los Security Groups en AWS están atados a una VPC específica, no son globales a la cuenta. Si tu cuenta tiene una VPC por defecto además de la que creaste para este ejercicio, es muy fácil terminar seleccionando —sin darte cuenta— un Security Group que pertenece a la VPC equivocada.

La solución: crear el Security Group de la RDS explícitamente dentro de la misma VPC donde vive la EC2, y volver a intentar la creación seleccionando esa VPC de forma consistente en todo el asistente.

Paso 7: el segundo error — el DB Subnet Group necesita dos zonas

Con el Security Group corregido, apareció otro obstáculo, esta vez silencioso: el asistente de RDS exige un DB Subnet Group con subredes en al menos dos zonas de disponibilidad distintas, por diseño de alta disponibilidad de AWS.

Como en el Paso 1 ambas subredes (pública y privada) quedaron en la misma AZ para calzar con el diagrama original, no había forma de armar ese grupo sin agregar una subred más.

La solución: crear una tercera subred (10.0.3.0/24) en una zona de disponibilidad diferente a las otras dos, y asociarla a la tabla de ruteo privada — para que se comporte exactamente igual que la primera subred privada: sin acceso directo desde internet, pero con salida vía NAT Gateway.

Creación del DB Subnet Group con dos subredes en zonas de disponibilidad distintas DB Subnet Group con la subred privada original y la nueva subred privada en otra AZ.

Con esto, el panorama de subredes quedó así:

SubredCIDRZonaTabla de ruteo
Pública10.0.1.0/24AZ-aPública → IGW
Privada 110.0.2.0/24AZ-aPrivada → NAT
Privada 210.0.3.0/24AZ-bPrivada → NAT

Paso 8: lanzar RDS en la subred privada

Con el Security Group y el Subnet Group correctos, la base de datos se creó sin más fricción: subred privada, acceso público: No, motor PostgreSQL.

Configuración final de conectividad al crear la instancia RDS RDS PostgreSQL en la VPC correcta, con acceso público deshabilitado.

Paso 9: el tercer error — no me dejaba conectar por SSH

Al intentar entrar a la EC2 con el botón “Conectar” del navegador (EC2 Instance Connect), la consola devolvió:

Failed to connect to your instance
Error establishing SSH connection to your instance. Try again later.

Error de EC2 Instance Connect al intentar conectarse desde el navegador Error de conexión SSH desde el navegador de AWS.

Las tres causas más comunes de este error, en orden de probabilidad:

  1. El Security Group no tiene el puerto 22 abierto. Hay que agregar una regla de entrada tipo SSH, con origen My IP (recomendado) o 0.0.0.0/0 solo para pruebas rápidas.
  2. La subred no tiene ruta real al Internet Gateway — se valida revisando que la tabla de ruteo asociada tenga la fila 0.0.0.0/0 → igw-xxxxxxxx.
  3. La instancia no tiene IP pública asignada — se soluciona asociando una Elastic IP desde la sección correspondiente.

Cuando el navegador sigue fallando aun con los tres puntos correctos (algo común en imágenes Ubuntu muy recientes donde el agente de Instance Connect no viene preinstalado), la alternativa robusta es conectarse directo desde una terminal local con la clave .pem descargada al crear la instancia:

chmod 400 mi-clave.pem
ssh -i "mi-clave.pem" ec2-user@TU_IP_PUBLICA_EC2

Paso 10: el cuarto “error” — apt no existe

Ya dentro de la instancia por SSH, el primer intento de instalar dependencias falló:

[ec2-user@ip-10-0-1-149 ~]$ sudo apt update
sudo: apt: command not found

El prompt ec2-user ya lo delataba: la instancia corre Amazon Linux, no Ubuntu/Debian — y Amazon Linux usa dnf como gestor de paquetes, no apt:

sudo dnf update -y
sudo dnf install -y nodejs
node -v
npm -v

Paso 11: script de prueba end-to-end (EC2 → RDS)

Con Node.js instalado, un script mínimo permite validar dos cosas a la vez desde el navegador: que la EC2 responde desde internet, y que desde ahí puede alcanzar la RDS por su IP privada.

// server.js — servidor HTTP mínimo que valida EC2 <-> RDS
const http = require("http");
const { Client } = require("pg");

const dbConfig = {
  host: "TU_ENDPOINT_RDS.rds.amazonaws.com",
  user: "postgres",
  password: "tu_password",
  database: "postgres",
  port: 5432,
};

async function testDatabaseConnection() {
  const client = new Client(dbConfig);
  try {
    await client.connect();
    await client.end();
    return { success: true, message: "Conexión exitosa a la RDS." };
  } catch (error) {
    return { success: false, message: error.message };
  }
}

const server = http.createServer(async (req, res) => {
  res.setHeader("Content-Type", "application/json");
  const dbStatus = await testDatabaseConnection();

  res.writeHead(dbStatus.success ? 200 : 500);
  res.end(
    JSON.stringify(
      {
        status: "EC2 en línea (acceso público OK)",
        database_connection: dbStatus,
      },
      null,
      2,
    ),
  );
});

server.listen(80, () => console.log("Servidor corriendo en el puerto 80"));
mkdir app-test && cd app-test
npm init -y
npm install pg
sudo node server.js

Con el puerto 80 habilitado también en el Security Group de la EC2, abrir http://TU_IP_PUBLICA_EC2 desde el navegador debería devolver el JSON de estado.

Paso 12: el quinto error — PostgreSQL rechazando la conexión

La primera ejecución del script devolvió esto en el navegador:

"database_connection": {
  "success": false,
  "message": "no pg_hba.conf entry for host \"10.0.1.149\", user \"postgres\", database \"postgres\", no encryption"
}

Respuesta JSON en el navegador mostrando el error de conexión SSL a PostgreSQL El error confirma que la red ya funciona: la EC2 sí llegó hasta la RDS. El rechazo es a nivel de PostgreSQL, no de red.

Este mensaje en realidad es una buena señal a medias: significa que no hubo timeout, o sea, la conexión de red EC2 → RDS funcionó perfecto (Security Groups y subredes correctos). El rechazo viene de PostgreSQL, que en RDS exige por defecto que las conexiones vayan cifradas:

const dbConfig = {
  host: "TU_ENDPOINT_RDS.rds.amazonaws.com",
  user: "postgres",
  password: "tu_password",
  database: "postgres",
  port: 5432,
  ssl: {
    rejectUnauthorized: false, // acepta el certificado administrado por AWS
  },
};

Con ese único bloque agregado y el servidor reiniciado, la respuesta cambió a lo esperado:

{
  "status": "EC2 en línea (acceso público OK)",
  "database_connection": {
    "success": true,
    "message": "Conexión exitosa a la RDS."
  }
}

Respuesta JSON final confirmando la conexión exitosa entre EC2 y RDS Resultado final: la arquitectura completa funcionando de punta a punta.

Tabla resumen: errores reales y su solución

ErrorCausa realSolución
DB instance and EC2 security group are in different VPCsSecurity Group seleccionado pertenece a otra VPC (a veces la default de la cuenta)Crear el Security Group explícitamente en la misma VPC que la RDS
No se puede crear el DB Subnet GroupLas subredes están todas en la misma zona de disponibilidadCrear una subred adicional en otra AZ y asociarla a la tabla de ruteo privada
Error establishing SSH connection (Instance Connect)Puerto 22 cerrado, falta ruta al IGW, o no hay IP pública asignadaRevisar Security Group, tabla de ruteo pública y Elastic IP; como alternativa, conectar por terminal con .pem
sudo: apt: command not foundLa AMI es Amazon Linux, no UbuntuUsar dnf en vez de apt
no pg_hba.conf entry ... no encryptionRDS exige conexión cifrada por defectoAgregar ssl: { rejectUnauthorized: false } en la configuración del cliente

Buenas prácticas antes de llevar esto a producción

Esta guía usa 0.0.0.0/0 para SSH y accesos rápidos porque el objetivo es validar la arquitectura sin fricción. Antes de un entorno real, conviene ajustar:

  • SSH restringido a IP fija o, mejor aún, reemplazarlo por AWS Systems Manager Session Manager, que elimina la necesidad de abrir el puerto 22 por completo.
  • Rotar credenciales de la base de datos fuera del script (variables de entorno o AWS Secrets Manager), nunca hardcodeadas como en el ejemplo.
  • Multi-AZ real para la RDS (no solo el Subnet Group), si la disponibilidad de la base de datos es crítica para el negocio.
  • HTTPS en la EC2 en vez de HTTP plano, típicamente detrás de un Application Load Balancer.

Conclusión

Lo que en el diagrama se ve como cuatro cajas y unas flechas, en la práctica implica resolver Security Groups atados a la VPC correcta, zonas de disponibilidad para el Subnet Group, permisos SSH, el gestor de paquetes correcto según la AMI, y el modo SSL que RDS exige por defecto. Ninguno de estos errores estaba “mal explicado” en la documentación — simplemente no aparecen hasta que los tocas en la práctica. Si te está pasando algo parecido montando tu propia infraestructura en AWS, lo más probable es que sea uno de los cinco errores de la tabla de arriba.

¿Tu equipo necesita ayuda diseñando o auditando una arquitectura de red en AWS? Agenda una asesoría técnica gratuita de 15 minutos y lo revisamos juntos.

¿Tienes un proyecto en mente?

Convierte tu idea en un producto real

Desarrollo web, aplicaciones a medida y consultoría tecnológica para empresas y startups. Cuéntame tu proyecto y te respondo en menos de 24 horas.

Solicitar presupuesto Ver LinkedIn