Angela Sofíá Osorio
Tiempo de lectura 10 minutes
Fecha de publicación
Desplegar una aplicación fullstack moderna no debería ser una pesadilla de configuraciones manuales. Como JavaScript/TypeScript Developer, sé que tener un pipeline de despliegue limpio, predecible y automatizado es esencial para llevar nuestros proyectos de entorno local a producción sin fricciones.
Para este tutorial, utilizaremos un VPS de Hostinger. Después de probar múltiples proveedores, Hostinger ofrece el equilibrio perfecto entre rendimiento, control total (acceso root) y facilidad de uso, lo que lo hace ideal para orquestar contenedores.
Nota especial: Si vas a seguir este tutorial y aún no tienes tu servidor, puedes usar mi enlace de afiliado para obtener un 20% de descuento adicional en la contratación de tu VPS.
En esta guía, desplegaremos un frontend en React (Vite) y un backend en Node.js (Express) utilizando Docker, configuraremos un proxy inverso con Nginx y certificados SSL, y automatizaremos todo el proceso utilizando GitHub Actions.
Requisitos Previos
Antes de comenzar, asegúrate de contar con lo siguiente:
- Un servidor VPS en Hostinger con Ubuntu (se recomienda la versión 24.04 LTS).
- Un dominio o subdominio configurado en tu panel de Hostinger apuntando a la IP pública de tu VPS (por ejemplo,
todo.midominio.com). - Una aplicación que desplegar, o puedes usar la que creamos en el video clonando el repo Aquí
¿Qué es un VPS?
Un VPS o Virtual Private Server (Maquina Virtual privada) es precisamente un servidor cuyos recursos están dedicados a ti. Mientras que la mayoría de Hostings compartidos siempre comparten el sistema operativo y hardware con otros usuarios, lo cual te limita para poder instalar tus propios programas o comandos.
Imagina que el Hosting compartido es como vivir con Roomies, tu rentas un departamento con otras personas y todos comparten los mismos espacios. El VPS es como tener tu propio departamento, aunque compartes la misma infraestrructura, el Edificio, nadie puede entrar y usar tu departamento. El VPS te da acceso al root, lo cual te permite instalar tu propio software y servicios.
Primeros pasos
Una vez que realices el pago de tu VPS en Hostinger. vamos a elegir Ubuntu Server
Al hacer esto se nos pedirá que creemos una contraseña y tendremos la posivbilidad de conectarnos vía SSH. No te preocupes, esto último, lo haremos más adelante.
A partir de ahora Hostinger te ofrecera servixcios adicionales que puedes o NO agregar, solo dale siguiente.
Si no habías elegido un plan y realizado tu pago, en esta parte te pedira que elijas uno, los meses a comprar y que realices el pago:
Una vez hecho el pago verás que Hostinger se pondrá a configurar tu VPS por lo que puedes salir de la página y Hostinger te enviará un email cuando termine(Igual puedes quedarte y esperar). Tardará aproximadamente 3 minutos.
Conectar nuestro SSH a la terminal
Ahora que Hostinger ha terminado de configurar nuestro VPS verás que nos muestra una bienvenida y la clave ssh. con ello avanzamos a la siguiente fase (Nota que no he censurado la clave ssh. Esto es porque al final dell tutorial eliminé por completo ese VPS así que no hay ningún riesgo).
Abre tu terminal favorita. en mi caso yo uso WARP con ZSH. Pega el ssh que te dio Hostinger y presiona ENTER:
Te va a preguntar si estás seguro de querer conectarte y tu escribes yes. Al hacer esto te va a pedir la contraseña que estableciste al comienzo.
Verás que te da la bienvenida y hemos logrado conectarnos al VPS de Hostinger correctamente:
Lo primero que haremos al entrar
Vamos a actualizar nuestro sistema haciendo uso de los comandos:
sudo apt update && sudo apt upgradeBashConfigurando un usario nuevo
Dado que actualmente estamos haciendo use del usario root, lo cual es una mala practica y puede ser muy peligroso por muchas razones. Vamos a crear un nuevo usario que tenga privilegios de sudo. Para ello camos a escribir el comando:
adduser sofidevBashAl presionar ENTER verás que kla terminal te solcita que agregues un nuevo password para este usuario. A continuación te pedira que lo repitas para confirmar y luego te pedirá algunos datos personales, los cuales puedes omitir presionando ENTER
Agregando nuestro usario a la lista de sudoers
Ahora que hemos creado nuestro usario, debemos agregarlo a la lista de sudoers. Con esto le estamos dando permiso a nuestro nuevo usario de ejecutar comandos con permisos de administrador, sudo. Vamos entonces a escibir el siguiente comando:
usermod -aG sudo <tu_usario>
#en mi caso es usermod -aG sudo sofidev BashAhora cambiemos de usario, para ello simplemente usaremos el comando:
su - <tu_usario>
#en mi caso su - sofidevBashInstalando Docker en nuestro VPS
Para poder instala Docker vamos a hacer uso del siguiente comando:
sudo apt install docker.io docker-compose-v2 -yBashUna vez instalada debemos reiniciar nuestro SSH para que los cambios surtan efecto.Para ello puedes usar el comando:
sudo rebootBashVerás que te desconecta de tu VPS por lo que hay que iniciar de nuevo pero ene sta ocación no copiaremos tal cual el SSH de hostinger, ya que este nos inicia como root, sino que vamos a cambiar la palabra root por nuestro usario. En mic aso sofidev. te pedirá la contraseña que le pusiste a ese usario:
ssh sofidev@2.25.165.190 #En tu caso será tu ip que te asignó hostingerBash¿Ya tienes tu app lista?
Llegó el momento de preparar nuestra app para deployment. Si no tienes una y quieres seguir los pasos de este tutorial, aquí te dejo el repositrorio del proyecto que hicimos. Cabe aclarar que ya viene completamente configurado y sin errores por lo que básicamente solo tendrás que seguir los pasos por puro amor al conocimiento.
Paso 1: La Orquestación con Docker
Utilizamos Docker Compose para definir y ejecutar nuestra aplicación multicontenedor. Esto garantiza que el entorno sea idéntico tanto en desarrollo local como en el servidor de Hostinger.
Crea un archivo docker-compose.yml en la raíz de tu proyecto.
services:
# database
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
# backend
backend:
image: node:20-alpine
working_dir: /app
volumes:
- ./backend:/app
ports:
- "3000:3000"
command: sh -c "npm install && npm run dev"
environment:
- DATABASE_URL=${DATABASE_URL}
depends_on:
- db
# frontend
frontend:
image: node:20-alpine
working_dir: /app
volumes:
- ./frontend:/app
ports:
- "5173:5173"
command: sh -c "npm install && npm run dev -- --host"
environment:
- VITE_API_URL=${VITE_API_URL:-http://localhost:3000}
depends_on:
- backend
volumes:
db_data:YAMLVariables de Entorno
Nunca subas contraseñas a tu repositorio de código. Crea un archivo .env en el mismo directorio raíz y luego agregalo a tu archivo gitignore
MYSQL_ROOT_PASSWORD=TuContraseñaSegura123
MYSQL_DATABASE=midatabase
DATABASE_URL=mysql://root:TuContraseñaSegura123@db:3306/midatabasePlaintextPaso 2: Ajustes del Frontend para Producción
Al acceder a una aplicación de Vite a través de un nombre de dominio en lugar de localhost, Vite bloqueará la petición por motivos de seguridad. Necesitamos permitir explícitamente nuestro dominio.
Actualiza tu archivo frontend/vite.config.js:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
react(),
tailwindcss(), //Dependencias usadas en este proyecto específico
],
server: {
host: true,
port: 5173,
allowedHosts: [
'todo.sofidev.site' //Remplaza con tu dominio real,
],
},
});JavaScriptActualiza las llamadas a la API en tu código de React para que apunten a la ruta segura que configuraremos en Nginx:
// Ejemplo en frontend/src/api.js
const API_URL = 'https://todo.midominio.com/api'; JavaScriptPara aplicar estos cambios, construye y levanta tus contenedores:
sudo docker compose up -d --buildBashPaso 3: Proxy Inverso con Nginx y Seguridad SSL
Nginx actuará como el recepcionista del servidor, recibiendo el tráfico en los puertos 80 y 443, y redirigiéndolo a los contenedores de Docker correspondientes para evitar problemas de «Contenido Mixto» (Mixed Content).
Instala Nginx:
sudo apt update
sudo apt install nginx -yBashCrea un nuevo bloque de configuración para tu sitio:
sudo nano /etc/nginx/sites-available/todo.midominio.comBashPega la siguiente configuración:
server {
listen 80;
server_name todo.sofidev.site;
location / {
proxy_pass http://localhost:5173;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Esta es la línea que agregamos más adelante en el video tutorial:
location /api/ {
proxy_pass http://localhost:3000/;
}
}NginxActiva el sitio y recarga Nginx:
sudo ln -s /etc/nginx/sites-available/todo.midominio.com /etc/nginx/sites-enabled/
sudo systemctl reload nginxBashAsegura la conexión con Let’s Encrypt (Certbot):
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d todo.midominio.comBashAsí debería lucir la configuración final de nginx en tu server vps:
server {
server_name todo.sofidev.site;
location / {
proxy_pass http://localhost:5173;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
#Esta es la línea que agregamos más adelante en el video tutorial:
location /api/ {
proxy_pass http://localhost:3000/;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/todo.sofidev.site/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/todo.sofidev.site/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = todo.sofidev.site) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name todo.sofidev.site;
return 404; # managed by Certbot
}NginxPaso 4: Automatización de Despliegues con GitHub Actions
Entrar al servidor por SSH cada vez que hay un cambio en el código no es escalable. Gracias a la flexibilidad de nuestro VPS en Hostinger, podemos automatizar esto mediante un pipeline CI/CD.
Primero, añade las credenciales de tu servidor en tu repositorio de GitHub, navegando a Settings > Secrets and variables > Actions:
VPS_HOST: La dirección IP pública de tu servidor de Hostinger.VPS_USERNAME: El usuario de tu servidor.VPS_PASSWORD: La contraseña de tu servidor.
Luego, crea el siguiente archivo en tu repositorio: .github/workflows/deploy.yml:
YAML
name: CI/CD - Deploy Todo List
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Login in to VPS
uses: appleboy/ssh-action@v1.0.0
env:
VPS_PASSWORD: ${{ secrets.VPS_PASSWORD }}
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USERNAME }}
password: ${{ secrets.VPS_PASSWORD }}
envs: VPS_PASSWORD
script: |
cd ~/todo-list
git pull origin main
echo "$VPS_PASSWORD" | sudo -S docker compose up -d --buildYAMLCon esta configuración, cada comando git push a la rama main activará una conexión SSH segura a tu VPS, descargará el código más reciente y reconstruirá los contenedores de Docker de manera automática.
Conclusión
Has transformado un servidor en blanco en una infraestructura moderna, segura y completamente automatizada. Esta es exactamente la arquitectura que se utiliza en entornos profesionales y, gracias al excelente rendimiento de los VPS de Hostinger, tienes espacio de sobra para escalar este proyecto o alojar aplicaciones adicionales.
Si este tutorial te fue útil, no olvides aplicar tu [20% de descuento en Hostinger ingresando aquí]. Para más contenido sobre desarrollo con JavaScript, TypeScript y DevOps, asegúrate de seguirme en mi canal de YouTube SofiDev.
Contents
- 1 Requisitos Previos
- 2 ¿Qué es un VPS?
- 3 Primeros pasos
- 4 Conectar nuestro SSH a la terminal
- 5 Configurando un usario nuevo
- 6 Instalando Docker en nuestro VPS
- 7 ¿Ya tienes tu app lista?
- 8 Paso 1: La Orquestación con Docker
- 9 Paso 2: Ajustes del Frontend para Producción
- 10 Paso 3: Proxy Inverso con Nginx y Seguridad SSL
- 11 Paso 4: Automatización de Despliegues con GitHub Actions

















