01Conceptos y glosario esencial
DigitalOcean tiene su propio vocabulario. Con estos términos claros, el resto de la guía fluye sin fricción.
| Término | Qué es | Equivalente mental |
|---|---|---|
| Droplet | Máquina virtual Linux (tu servidor): CPU, RAM, disco y región. | Un VPS / tu "PC en la nube" |
| Managed Database | PostgreSQL administrado por DO: backups, parches y alta disponibilidad. | Base de datos sin preocupaciones |
| App Platform | Construye y despliega desde tu repo Git (PaaS). | Como Vercel / Heroku en DO |
| Spaces | Almacenamiento de objetos compatible con S3. | Disco para imágenes y archivos |
| Snapshot | Foto completa del Droplet para restaurar o clonar. | Copia de seguridad de sistema |
| Reserved IP | IP fija reasignable entre Droplets sin tocar el DNS. | IP estática móvil |
| Project | Carpeta lógica para agrupar recursos por cliente o app. | Una carpeta de proyecto |
| Región | Ubicación física (NYC, AMS, FRA, LON…). | Dónde vive tu servidor |
NYC1/NYC3 (Nueva York) suele dar buena latencia. Para Europa, AMS3 (Ámsterdam), FRA1 (Fráncfort) o LON1 (Londres). No hay datacenter en Sudamérica, así que NYC es el equilibrio habitual.02Crear cuenta, pago y facturación
Regístrate
En digitalocean.com con correo, GitHub o Google. Verifica el correo.
Agrega método de pago
Tarjeta o PayPal. DO pide una verificación inicial pequeña (se reembolsa). Sin pago válido no se crean Droplets.
Entiende la facturación por horas
Se cobra por hora de uso con tope mensual. Apagar el Droplet NO detiene el cobro: para dejar de pagar hay que destruirlo (haz un snapshot antes si quieres conservarlo).
Activa alertas de presupuesto
En Billing → Billing alerts define un monto (p. ej. USD 30) para recibir aviso si el gasto lo supera.
Organiza con Projects
Un Project por cliente o app: cada Droplet, base de datos y dominio queda agrupado y es fácil de rastrear.
03Crear tu primer Droplet
El Droplet es tu servidor. Lo crearemos con Ubuntu, la distribución más documentada y la que asumen casi todos los tutoriales de Django y Node.
Antes de empezar: crea tu llave SSH
La forma segura de entrar es con llave SSH, no con contraseña. Genera el par de llaves en tu computadora:
$ ssh-keygen -t ed25519 -C 'tu-correo@tema.com.pe' # Enter para la ruta por defecto (~/.ssh/id_ed25519). Opcional: passphrase $ cat ~/.ssh/id_ed25519.pub ssh-ed25519 AAAAC3NzaC1lZDI1... tu-correo@tema.com.pe
ssh-keygen. La llave pública (.pub) se comparte; la privada nunca se comparte ni se sube a ningún repositorio.Crear el Droplet en el panel
Create → Droplets
Botón verde Create (arriba a la derecha) → Droplets.
Región
La más cercana a tus usuarios. Para LATAM:
New York.Imagen del SO
Ubuntu 24.04 (LTS) x64.
Tamaño
Shared CPU → Basic → Regular. Para 1–2 apps: 2 GB RAM / 1 vCPU / 50 GB (~USD 12/mes). Django + Postgres agradecen 2 GB.
Autenticación
SSH Key → Add SSH Key y pega tu llave pública. Evita la opción de contraseña.
Nombre y proyecto
Un hostname claro (
web-prod-01) y asígnalo a un Project. Activa Improved metrics.Create Droplet
En ~1 minuto tendrás una IP pública (ej.
159.65.xx.xx). Cópiala.
$ ssh root@159.65.XX.XX # Primera vez: pregunta la huella del host → escribe: yes Welcome to Ubuntu 24.04 LTS ... root@web-prod-01:~#
root@web-prod-01:~# significa que ya controlas el servidor. Todo lo siguiente se ejecuta dentro de esta sesión SSH.04Asegurar el servidor
Un Droplet recién creado está expuesto a internet como root. Cinco minutos de configuración evitan el 90% de los problemas.
1 · Actualiza el sistema
# apt update && apt upgrade -y
2 · Crea un usuario con sudo (deja de usar root)
# adduser deploy # define contraseña (Enter en el resto) # usermod -aG sudo deploy # permisos de administrador # rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
3 · Activa el firewall (UFW)
$ sudo ufw allow OpenSSH # puerto 22 (SSH) — ¡no te encierres fuera! $ sudo ufw allow 'Nginx Full' # puertos 80 y 443 $ sudo ufw enable $ sudo ufw status
OpenSSH antes de ufw enable. Si activas el firewall sin permitir SSH, perderás el acceso y tendrás que usar la consola de recuperación del panel.05Dominios y subdominios (DNS)
Aquí conectas tus dominios con el servidor. La idea central: un registro A asocia un nombre (dominio o subdominio) con la IP de tu Droplet. Un solo servidor puede responder a decenas de subdominios.
Paso 1 · Apunta el dominio a los nameservers de DO
En el panel de donde compraste el dominio (Namecheap, GoDaddy, Hostinger…), cambia los nameservers:
ns1.digitalocean.com ns2.digitalocean.com ns3.digitalocean.com
Paso 2 · Crea los registros en DigitalOcean
Panel → Networking → Domains → añade tema.com.pe y crea los registros:
| Tipo | Hostname | Valor | Resultado |
|---|---|---|---|
| A | @ | 159.65.XX.XX | tema.com.pe |
| A | www | 159.65.XX.XX | www.tema.com.pe |
| A | app | 159.65.XX.XX | Subdominio app.tema.com.pe |
| A | api | 159.65.XX.XX | Subdominio api.tema.com.pe |
$ dig +short app.tema.com.pe 159.65.XX.XX # En Windows PowerShell: > Resolve-DnsName tema.com.pe
06Instalar el stack
Con el servidor asegurado y el DNS apuntando, instalamos las piezas. Nginx será el portero que recibe todo el tráfico web y lo reparte a cada app.
Nginx (servidor web / reverse proxy)
$ sudo apt install -y nginx $ systemctl status nginx # debe verse 'active (running)'
Visita http://159.65.XX.XX: verás "Welcome to nginx". Eso confirma firewall y web server.
Python (Django) y Node (React/Next)
# Python + compilación (Django + psycopg2) $ sudo apt install -y python3 python3-venv python3-pip build-essential libpq-dev # Node.js LTS (repositorio oficial) $ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - $ sudo apt install -y nodejs $ node --version && npm --version v22.x.x 10.x.x
PostgreSQL (opción autogestionada)
$ sudo apt install -y postgresql postgresql-contrib $ sudo -u postgres psql # consola de Postgres
CREATE DATABASE miapp; CREATE USER miapp_user WITH PASSWORD 'una-clave-fuerte'; ALTER ROLE miapp_user SET client_encoding TO 'utf8'; ALTER ROLE miapp_user SET timezone TO 'America/Lima'; GRANT ALL PRIVILEGES ON DATABASE miapp TO miapp_user; \q
07Desplegar Django en producción
Arquitectura estándar: Nginx recibe HTTP → Gunicorn ejecuta Django → PostgreSQL guarda los datos. Nginx sirve los estáticos y systemd mantiene todo vivo.
1 · Sube el código y crea el entorno
$ cd /home/deploy $ git clone https://github.com/tu-usuario/mi-django-app.git $ cd mi-django-app $ python3 -m venv venv && source venv/bin/activate (venv) $ pip install -r requirements.txt (venv) $ pip install gunicorn psycopg2-binary
2 · Ajusta settings.py y migra
ALLOWED_HOSTS = ['app.tema.com.pe', '159.65.XX.XX'] DEBUG = False DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'miapp', 'USER': 'miapp_user', 'PASSWORD': 'una-clave-fuerte', # usa variables de entorno 'HOST': 'localhost', 'PORT': '5432', } } STATIC_ROOT = '/home/deploy/mi-django-app/staticfiles'
(venv) $ python manage.py migrate (venv) $ python manage.py collectstatic --noinput (venv) $ python manage.py createsuperuser
3 · Servicio systemd para Gunicorn
[Unit] Description=gunicorn daemon para mi-django-app After=network.target [Service] User=deploy Group=www-data WorkingDirectory=/home/deploy/mi-django-app ExecStart=/home/deploy/mi-django-app/venv/bin/gunicorn \ --workers 3 \ --bind unix:/home/deploy/mi-django-app/app.sock \ miproyecto.wsgi:application [Install] WantedBy=multi-user.target
$ sudo systemctl start gunicorn $ sudo systemctl enable gunicorn # arranca solo al reiniciar $ sudo systemctl status gunicorn
4 · Bloque de Nginx para el subdominio
server {
listen 80;
server_name app.tema.com.pe;
location /static/ {
alias /home/deploy/mi-django-app/staticfiles/;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/deploy/mi-django-app/app.sock;
}
}
$ sudo ln -s /etc/nginx/sites-available/app.tema.com.pe /etc/nginx/sites-enabled/ $ sudo nginx -t # debe decir: test is successful $ sudo systemctl restart nginx
http://app.tema.com.pe muestra tu Django. Falta el HTTPS (sección 10). Tras cada cambio de código: git pull → pip install → migrate → collectstatic → sudo systemctl restart gunicorn.08Desplegar React y Next.js
Se despliegan distinto porque son cosas distintas: React (Vite/CRA) compila a archivos estáticos que Nginx sirve directo; Next.js corre un servidor Node vivo (SSR/API), gestionado con PM2.
Caso A · React — sitio estático
$ cd /home/deploy $ git clone https://github.com/tu-usuario/mi-react-app.git $ cd mi-react-app && npm install && npm run build
server { listen 80; server_name web.tema.com.pe; root /home/deploy/mi-react-app/dist; index index.html; location / { try_files $uri $uri/ /index.html; # clave para React Router (SPA) } }
try_files. En una SPA, toda ruta (/panel, /perfil) debe servir index.html para que React Router se encargue. Sin esa línea, recargar una ruta interna da 404.Caso B · Next.js — servidor Node con PM2
$ sudo npm install -g pm2 $ cd /home/deploy/mi-next-app $ npm install && npm run build $ pm2 start npm --name 'next-app' -- start # Next en :3000 $ pm2 save $ pm2 startup # ejecuta la línea que imprime
server { listen 80; server_name next.tema.com.pe; location / { proxy_pass http://localhost:3000; 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; } }
:3000, otra en :3001…) y cada subdominio tiene su bloque de Nginx apuntando a la app correcta. Así conviven Django, React y Next en el mismo servidor.| Comando PM2 | Para qué |
|---|---|
pm2 list | Ver todas las apps y su estado |
pm2 logs next-app | Logs en vivo |
pm2 restart next-app | Reiniciar tras un despliegue |
pm2 monit | Monitor de CPU/RAM por proceso |
09PostgreSQL: gestionado vs. autogestionado
Postgres en el mismo Droplet es lo más barato para empezar. Cuando el proyecto es serio, la Managed Database ahorra dolores de cabeza: backups automáticos, parches y failover.
| En el Droplet | Managed Database | |
|---|---|---|
| Costo | Incluido en el Droplet | Desde ~USD 15/mes aparte |
| Backups automáticos | Los configuras tú | Incluidos (point-in-time) |
| Parches / actualizaciones | Manuales | Automáticos |
| Alta disponibilidad | No | Nodos de reserva opcionales |
| Ideal para | Prototipos, presupuesto ajustado | Producción real, datos críticos |
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'defaultdb', 'USER': 'doadmin', 'PASSWORD': 'xxxxxxxx', 'HOST': 'db-postgresql-nyc1-xxxx.ondigitalocean.com', 'PORT': '25060', 'OPTIONS': {'sslmode': 'require'}, } }
.env con python-dotenv) y añade ese archivo a .gitignore. En Settings → Trusted Sources de la Managed DB, permite solo tu Droplet.10HTTPS gratis con Let's Encrypt
Sin candado, los navegadores marcan el sitio como "no seguro". Certbot instala un certificado gratuito y lo renueva solo.
$ sudo apt install -y certbot python3-certbot-nginx $ sudo certbot --nginx -d app.tema.com.pe -d www.tema.com.pe # Pide correo, aceptas términos y eliges redirigir HTTP → HTTPS $ sudo certbot renew --dry-run # prueba la renovación automática Congratulations! ... https://app.tema.com.pe
certbot más. Repite sudo certbot --nginx -d nuevo.tema.com.pe, o incluye varios -d en un solo comando. Certbot edita Nginx y programa la renovación por ti.11App Platform: desplegar sin servidor
Si prefieres velocidad sobre control, App Platform es lo más parecido a Vercel/Heroku dentro de DO: conectas GitHub y cada git push despliega solo. Sin SSH, sin Nginx, sin Certbot.
Create → Apps
Conecta GitHub/GitLab y elige repositorio y rama (
main).DO detecta el tipo
Reconoce Django, Node/Next, React… Confirma los comandos de build y run.
Variables de entorno
Agrega
SECRET_KEY,DATABASE_URL… como encrypted env vars. Vincula una Managed DB con un clic.Dominio
Da una URL
*.ondigitalocean.appcon HTTPS. Añade tu dominio en Settings → Domains y crea el CNAME indicado.Deploy
Cada push reconstruye y redespliega automáticamente.
12Administrar, monitorear y escalar
Backups y snapshots
Droplet Backups
~20% del costo. Copias semanales automáticas para restaurar todo el servidor.
Snapshots
Foto puntual del Droplet. Úsalo antes de cambios grandes o para clonar.
Managed backups
Backups diarios con restauración point-in-time, sin que hagas nada.
Monitoreo
La pestaña Monitoring muestra CPU, memoria, disco y red. Crea alertas (Monitoring → Create Alert Policy). Dentro del servidor:
$ htop # procesos, CPU y RAM en vivo $ df -h # espacio en disco $ free -h # memoria disponible $ sudo journalctl -u gunicorn -n 50 --no-pager # logs de Django $ sudo tail -f /var/log/nginx/error.log # errores de Nginx
Escalar cuando crezcas
| Necesidad | Solución en DO |
|---|---|
| Más CPU/RAM | Resize del Droplet (el de solo CPU/RAM es reversible; el de disco no) |
| Más almacenamiento | Conectar un Volume (disco adicional) |
| Mucho tráfico | Varios Droplets tras un Load Balancer |
| Archivos / media | Spaces (S3) + CDN |
| IP fija reasignable | Reserved IP para migrar sin tocar el DNS |
Actualizar una app ya desplegada
# Django $ cd /home/deploy/mi-django-app && source venv/bin/activate $ git pull && pip install -r requirements.txt $ python manage.py migrate && python manage.py collectstatic --noinput $ sudo systemctl restart gunicorn # Next.js $ cd /home/deploy/mi-next-app $ git pull && npm install && npm run build $ pm2 restart next-app
13Checklist de seguridad
Marca esto en cada servidor nuevo. Son las prácticas que separan un servidor sano de uno comprometido.
- Acceso solo por llave SSH. Desactiva contraseñas en
/etc/ssh/sshd_config(PasswordAuthentication no). - No usar root a diario. Trabaja con
deployysudo. - Firewall activo (UFW y/o Cloud Firewall): solo 22, 80 y 443.
- Base de datos no expuesta. Postgres en
localhost, o Managed DB restringida a Trusted Sources. - HTTPS en todo con Certbot y redirección de HTTP a HTTPS.
- Secretos fuera de Git:
SECRET_KEY, contraseñas y tokens en variables de entorno. DEBUG = Falseen producción, conALLOWED_HOSTSbien definido.- Actualizaciones regulares; considera
unattended-upgradesyfail2ban. - Backups probados: actívalos y verifica que sabes restaurarlos.
.env o settings.py con la SECRET_KEY a un repositorio. Revisa tu .gitignore antes del primer push.14Costos estimados
Precios orientativos para planificar. Verifica siempre los valores actuales en el panel.
| Recurso | Especificación | Precio aprox. | Para qué |
|---|---|---|---|
| Droplet básico | 1 GB · 1 vCPU · 25 GB | USD 6/mes | Pruebas, 1 app pequeña |
| Droplet recomendado | 2 GB · 1 vCPU · 50 GB | USD 12/mes | 1–2 apps + Postgres local |
| Droplet mediano | 4 GB · 2 vCPU · 80 GB | USD 24/mes | Varias apps y subdominios |
| Managed PostgreSQL | 1 GB · nodo único | ~USD 15/mes | BD en producción |
| Backups del Droplet | ~20% del Droplet | +USD 1.20–2.40/mes | Copias automáticas |
| Spaces | 250 GB + CDN | ~USD 5/mes | Imágenes y archivos |
| App Platform | Basic por componente | desde ~USD 5/mes | Deploy sin servidor |
15Referencia rápida de comandos
# ── Conexión ── ssh deploy@IP # ── Sistema ── sudo apt update && sudo apt upgrade -y df -h ; free -h ; htop # ── Nginx ── sudo nginx -t sudo systemctl restart nginx sudo tail -f /var/log/nginx/error.log # ── Django / Gunicorn ── sudo systemctl restart gunicorn sudo journalctl -u gunicorn -n 50 --no-pager source venv/bin/activate # ── Next / PM2 ── pm2 list ; pm2 logs ; pm2 restart next-app ; pm2 monit # ── Firewall / HTTPS ── sudo ufw status sudo certbot --nginx -d sub.tema.com.pe sudo certbot renew --dry-run # ── PostgreSQL ── sudo -u postgres psql # \l listar \du usuarios \q salir