Skip to content

Instantly share code, notes, and snippets.

@melizeche
Last active November 24, 2025 16:35
Show Gist options
  • Select an option

  • Save melizeche/8370d67c8873be83249412ce52b70e69 to your computer and use it in GitHub Desktop.

Select an option

Save melizeche/8370d67c8873be83249412ce52b70e69 to your computer and use it in GitHub Desktop.

🧰 Taller Práctico: De la Terminal al Deploy con Git y VPS (Versión Mejorada)

🎯 Objetivo General

Que aprendas a usar la terminal Linux, manejes el control de versiones con Git y despliegues un proyecto real (tanto estático como dinámico) en un servidor VPS. Vas a entender todo el proceso de desarrollo a producción, perdiéndole el miedo a los servidores, añadiendo capas de seguridad esencial y sin depender de plataformas cerradas.


zero: 📋 Prerrequisitos (¡Antes de empezar!)

Mirar antes las instrucciones del taller: https://gist.github.com/melizeche/e35612c29e1c5565d0ff3bf0b2d3fdf3

Para este taller, necesitás:

  • Un Servidor VPS: Un VPS limpio (ej. Ubuntu 22.04) de un proveedor como DigitalOcean, Linode, Vultr, Hetzner, etc.
  • Acceso Root: La IP de tu servidor y la contraseña de root (o acceso por SSH key).
  • Un Cliente SSH: La terminal (en Linux/Mac) o un cliente como PuTTY o Windows Terminal (en Windows).
  • Una Cuenta de GitHub: Para la parte de Git (opcional pero recomendado).
  • (Opcional) Un Dominio: Si querés configurar un dominio real con HTTPS. Para pruebas, podés usar la IP.

🧩 1. Fundamentos de Linux y Preparación del Servidor

Acá es donde perdemos el miedo a la terminal. Vas a aprender a navegar, editar archivos, gestionar permisos y procesos. Lo más importante: asegurar el servidor antes de subir nada. (Créeme, no querés aprender esto de la manera difícil)


📂 Navegación de directorios

pwd          # Muestra la ruta actual
ls           # Lista archivos del directorio actual
ls -la       # Lista todo (l=largo, a=all/ocultos)
cd carpeta   # Entra a una carpeta
cd ..        # Sube un nivel
cd ~         # Vuelve al directorio "home" del usuario
cd /var/log  # Va a una ruta absoluta

Probá esto:

  1. Verificá en qué directorio estás con pwd
  2. Andá a tu home: cd ~
  3. Creá una carpeta proyecto_demo y entrá
  4. Creá subcarpetas: mkdir src docs tests

🧱 Manejo de archivos y carpetas

# Creación
touch archivo.txt              # Crear archivo vacío
mkdir carpeta_nueva            # Crear carpeta
echo "Hola Mundo" > hola.txt   # Crear archivo y escribir texto
echo "Linea 2" >> hola.txt     # Añadir texto al final de un archivo

# Visualización
cat hola.txt                   # Mostrar contenido (para archivos cortos)

# Edición
nano hola.txt                  # Editar archivo con 'nano' (Ctrl+X para salir)

# Copiar y Mover
cp hola.txt copia.txt          # Copiar archivo
mv copia.txt carpeta_nueva/    # Mover archivo
mv carpeta_nueva/copia.txt .   # Mover archivo al directorio actual "."

# Borrado
rm hola.txt                    # Borrar archivo (pide confirmación)
rm -f hola.txt                 # Borrar forzadamente (force)
rmdir carpeta_nueva            # Borrar carpeta (solo si está vacía)
rm -r carpeta_llena            # Borrar carpeta y todo su contenido (recursive)

# Enlaces (¡Importante para Nginx!)
ln -s /ruta/original /ruta/enlace  # Crear un enlace simbólico

Ahora te toca:

  1. Creá index.html en src con el texto "Página" (echo "Página" > src/index.html).
  2. Editalo con nano src/index.html y añadí " web". Guardá (Ctrl+O) y Salí (Ctrl+X).
  3. Movelo a la carpeta docs.
  4. Creá un enlace simbólico llamado atajo.html que apunte a docs/index.html.
  5. Borrá la carpeta tests (que está vacía) y luego borrá atajo.html.

🔒 Permisos y propiedad

ls -l                    # Ver permisos (ej. -rwxr-xr-x)
whoami                   # Ver qué usuario sos
sudo <comando>           # Ejecutar un comando como Super-Usuario (root)

# Cambiar permisos
chmod +x script.sh       # Añadir permiso de ejecución (eXecutable)
chmod 644 archivo.txt    # (Dueño: RW, Grupo: R, Otros: R)
chmod 755 carpeta        # (Dueño: RWX, Grupo: RX, Otros: RX)

# Cambiar propietario
chown usuario:grupo archivo.txt

Desafío:

Creá un archivo run.sh con echo "Corriendo..." e intentá ejecutarlo con ./run.sh. Va a fallar. ¿Por qué? Porque no tiene permisos de ejecución. Arreglalo con chmod +x run.sh y probá de nuevo.

📝 Ejemplo práctico: Script de setup de proyecto

#!/bin/bash
# setup.sh - Configuración inicial del proyecto

echo "Creando estructura de proyecto..."

mkdir -p proyecto/{src,docs,tests,logs}
touch proyecto/README.md
touch proyecto/.gitignore

# Ignorar archivos comunes
echo "*.log" >> proyecto/.gitignore

# Python
echo "__pycache__/" >> proyecto/.gitignore
echo "*.py[cod]" >> proyecto/.gitignore
echo "venv/" >> proyecto/.gitignore
echo "env/" >> proyecto/.gitignore
echo ".venv/" >> proyecto/.gitignore

# Node.js (si usás frontend)
echo "node_modules/" >> proyecto/.gitignore

echo "✅ Proyecto creado en ./proyecto"
ls -la proyecto

Para usarlo:

nano setup.sh          # Pegá el código
chmod +x setup.sh      # Dale permisos
./setup.sh             # Ejecutalo

💡 Nota: El #!/bin/bash (shebang) le dice al sistema qué intérprete usar.


🔍 Búsqueda y visualización

# Búsqueda
find . -name "*.py"          # Buscar archivos por nombre
grep "Hola" *.txt            # Buscar texto dentro de archivos
grep -r "Hola" .             # Buscar texto recursivamente

# Visualización de archivos grandes
less /var/log/syslog         # Ver archivo interactivamente (q para salir)
head -n 20 archivo.txt       # Ver las primeras 20 líneas
tail -n 20 archivo.txt       # Ver las últimas 20 líneas
tail -f /var/log/nginx/access.log  # Ver el final en tiempo real (Follow)

Probá buscar "Hola" en tus archivos con grep y usá head para ver las primeras líneas de cualquier archivo. Son comandos que vas a usar TODO el tiempo.


⚙️ Procesos y monitoreo del sistema

ps aux            # Ver todos los procesos activos
top               # Monitoreo en tiempo real (q para salir)
htop              # 'top' mejorado (requiere instalación)
sleep 60 &        # Ejecutar en segundo plano (el &)
jobs              # Ver trabajos en segundo plano
kill <PID>        # Terminar proceso por su ID
killall sleep     # Terminar todos los procesos con ese nombre

# Uso del sistema
df -h             # Ver uso del disco (Human-readable)
free -h           # Ver uso de RAM (Human-readable)

Mini desafío:

Ejecutá sleep 300 & (el & lo manda a segundo plano), buscá el proceso con ps aux | grep sleep, y matálo con kill <PID>. Después chequeá el espacio en disco con df -h - esto es clave en un VPS.

📊 Ejemplo práctico: Script de monitoreo del servidor

#!/bin/bash
# health_check.sh - Monitoreo básico del servidor

echo "=== 🏥 Estado del Servidor ==="
echo "Fecha: $(date)"
echo ""

echo "💾 Uso de Disco:"
df -h / | tail -1

echo ""
echo "🧠 Uso de Memoria:"
free -h | grep Mem

echo ""
echo "⚙️  Servicios activos:"
systemctl is-active nginx 2>/dev/null && echo "  ✅ Nginx: activo" || echo "  ❌ Nginx: inactivo"
systemctl is-active ssh 2>/dev/null && echo "  ✅ SSH: activo" || echo "  ❌ SSH: inactivo"

echo ""
echo "👥 Últimos 5 logins:"
last -n 5

Uso: Ejecutalo periódicamente para verificar el estado de tu VPS.


📦 Gestión de Paquetes (APT en Ubuntu/Debian)

sudo apt update              # Actualiza la lista de paquetes disponibles
sudo apt upgrade -y          # Actualiza todos los paquetes instalados
sudo apt install htop nginx  # Instala paquetes
sudo apt remove htop         # Desinstala un paquete
sudo apt search "web server" # Busca paquetes

Actualizá los paquetes con apt update e instalá htop. Es como top pero mucho mejor. Una vez que lo uses, no vas a volver atrás.


🌐 Redes y Descargas

ping google.com              # Verifica conectividad (Ctrl+C para parar)
wget https://url/archivo.zip # Descarga un archivo
curl https://google.com      # Muestra el contenido de una URL en terminal
curl -L https://url          # Sigue redirecciones

Probá ping google.com para verificar conectividad y curl https://google.com para ver el HTML crudo. curl es una navaja suiza para debugging.


🗜️ Compresión y Archivos

# Comprimir
tar -czvf backup.tar.gz /ruta/a/carpeta
# (c=create, z=gzip, v=verbose, f=file)

# Descomprimir
tar -xzvf backup.tar.gz
# (x=extract)

Comprimí tu carpeta proyecto_demo en un .tar.gz. Esto lo vas a usar para backups todo el tiempo.

💾 Ejemplo práctico: Script de backup automático

#!/bin/bash
# backup.sh - Backup automático de proyecto

FECHA=$(date +%Y%m%d_%H%M%S)
ORIGEN="/home/deploy/mi_proyecto_live"
DESTINO="/home/deploy/backups"

echo "📦 Creando backup..."

mkdir -p $DESTINO
tar -czf $DESTINO/backup_$FECHA.tar.gz $ORIGEN

echo "✅ Backup creado: backup_$FECHA.tar.gz"

# Borrar backups de más de 7 días
find $DESTINO -name "backup_*.tar.gz" -mtime +7 -delete
echo "🧹 Backups antiguos eliminados"

Uso avanzado: Podés automatizar este script con cron para que corra diariamente:

# Editar crontab
crontab -e

# Añadir esta línea para backup diario a las 2 AM
0 2 * * * /home/deploy/backup.sh

⚡ Atajos útiles

  • Tab → Autocompletado de comandos y rutas
  • Ctrl + C → Detener proceso actual
  • Ctrl + R → Buscar en el historial de comandos
  • !! → Repetir el último comando (útil con sudo !!)
  • Ctrl + D → Salir de la sesión (exit)
  • clear (o Ctrl + L) → Limpiar la pantalla

🧭 2. Control de versiones con Git

Git es una de esas herramientas que al principio parece complicada, pero después no podés vivir sin ella. Acá vamos a ver el flujo básico: commits, ramas, y cómo conectarlo a GitHub.


⚙️ Configuración inicial

git config --global user.name "Tu Nombre"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main  # Usar 'main' como rama por defecto

🏗️ Flujo básico de trabajo

git init                 # iniciar repositorio local
git status               # ver cambios

✨ Mejora: Creá un .gitignore antes del primer add. touch .gitignore (Añade node_modules/, .env, *.log, etc. según tu proyecto)

git add .                # agregar archivos al staging
git commit -m "Primer commit"
git log                  # historial de commits
git restore archivo.txt  # descartar cambios en un archivo
git restore --staged archivo.txt  # quitar del staging

Ejercicio:

  1. Creá README.md y .gitignore
  2. Escribí algo en el README y hacétu primer commit

🌿 Ramas y merges

git switch -c nueva-feature
echo "Cambio nuevo" > feature.txt
git add feature.txt
git commit -m "Nueva feature"
git switch main
git merge nueva-feature
git branch -d nueva-feature  # borrar rama ya mergeada

Ahora probá crear una rama dev, agregá algún archivo, y mergeálo a main. Así es como se trabaja en equipos.


🔍 Explorando el historial

git log --oneline --graph --all  # visualizar historial compacto con ramas
git show <commit-hash>           # ver detalles de un commit específico
git diff                         # ver cambios no staged
git diff --staged                # ver cambios en staging

Usá git log --oneline --graph --all - para ver el árbol de commits. Yo tengo esto como alias git lg.


↩️ Deshaciendo cambios

git commit --amend -m "Mensaje corregido"  # modificar el último commit
git revert <commit-hash>         # deshacer un commit creando uno nuevo
git reset --soft HEAD~1          # deshacer último commit, mantener cambios
git reset --hard HEAD~1          # deshacer último commit, BORRAR cambios (¡cuidado!)

⚠️ Ojo acá: git reset --hard borra cambios SIN vuelta atrás. Yo una vez perdí 2 horas de trabajo por esto. No seas yo.

Hacé un commit con un typo en el mensaje y corregilo con --amend. Es re útil.


💾 Guardado temporal (Stash)

git stash                        # guardar cambios temporalmente
git stash list                   # ver stashes guardados
git stash pop                    # recuperar último stash
git stash apply stash@{0}        # aplicar un stash específico
git stash drop                   # borrar último stash

Caso de uso: Estás trabajando en algo, pero necesitás cambiar de rama urgentemente sin hacer commit.

Ejercicio:

  1. Hacé cambios en un archivo sin commitear
  2. Guardalos con git stash
  3. Verificá que el directorio está limpio (git status)
  4. Recuperalos con git stash pop

🔄 Sincronización avanzada con remoto

git fetch origin                 # descargar cambios sin mergear
git pull origin main             # fetch + merge (equivale a fetch + merge)
git push origin --delete rama    # borrar rama remota
git remote -v                    # ver remotos configurados
git branch -r                    # ver ramas remotas

Pro tip: Usá git fetch + git log --oneline --graph --all para ver qué hay en el remoto antes de hacer pull. Me salvó de varios conflictos.


🏷️ Tags y releases

git tag v1.0.0                   # crear tag ligero
git tag -a v1.0.0 -m "Release 1.0"  # tag anotado (recomendado)
git push origin v1.0.0           # subir tag específico
git push origin --tags           # subir todos los tags
git tag -l                       # listar tags

Cuando hagas tu primer release, creá un tag v0.1.0 y subílo. Los tags son perfectos para marcar versiones.


⚔️ Resolviendo conflictos

Cuando dos personas modifican la misma línea de código:

git switch main
git pull origin main
git switch mi-rama
git merge main
# ¡Conflicto!

Pasos para resolver:

  1. Abrí el archivo en conflicto. Verás marcas como:
<<<<<<< HEAD
Tu código
=======
Código de la otra persona
>>>>>>> main
  1. Editá el archivo manualmente, decidí qué código mantener y borrá las marcas (<<<<<<<, =======, >>>>>>>)
  2. Agregá el archivo resuelto:
git add archivo-resuelto.txt
git commit  # completar el merge

Ejercicio (importante):

Creá un conflicto a propósito: modificá la misma línea en dos ramas diferentes e intentá mergear. Vas a ver las marcas <<<<<<< y >>>>>>>. Resolvé el conflicto manualmente. Esto te va a pasar en la vida real, mejor practicarlo ahora.


⚡ Aliases y productividad

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.unstage "restore --staged"

Ahora podés usar: git st, git lg, git unstage archivo.txt, etc.

Configurá algunos aliases. Mis favoritos son git st para status y git lg para el log lindo. Ahorra muchísimo tiempo.


🌐 Repositorio remoto (GitHub/GitLab)

git remote add origin [email protected]:usuario/mi_proyecto.git
git push -u origin main

Ejercicio:

  • Creá un repositorio en GitHub y subí tu proyecto

☁️ 3. Deploy en un VPS con Git

Acá viene lo bueno. Vamos a conectar tu VPS, asegurarlo (IMPORTANTE), y configurar un deploy automático con Git. Cada vez que hagas git push production main, tu código va a estar en vivo.


🖥️ 1. Preparar y Securizar el Servidor

Conectate por SSH como root:

ssh root@ip_vps

Actualizar e instalar dependencias:

apt update && apt upgrade -y
# Añadimos Python, pip y venv, esenciales para Django
apt install git nginx python3-pip python3-venv -y
adduser deploy
usermod -aG sudo deploy

Configurar acceso SSH sin contraseña: (Hacé esto en tu máquina local, no en el servidor)

ssh-keygen -t ed25519
ssh-copy-id deploy@ip_vps

Ahora, salí (exit) y volvé a entrar como deploy: ssh deploy@ip_vps.


🔐 2. Securización Básica

(Conectado como deploy@ip_vps)

Firewall con UFW:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full' # Permite HTTP (80) y HTTPS (443)
sudo ufw enable

Deshabilitar login de root por SSH:

sudo nano /etc/ssh/sshd_config

Buscá y cambiá estas líneas:

PermitRootLogin no
PasswordAuthentication no

(Asegurate de que ssh-copy-id funcionó antes de deshabilitar passwords, sino te quedás afuera)

Reiniciá SSH:

sudo systemctl restart sshd

📦 3. Crear repositorio bare en el VPS

(Conectado como deploy@ip_vps)

mkdir -p ~/repos/mi_proyecto.git
cd ~/repos/mi_proyecto.git
git init --bare

🪝 4. Configurar hook de deploy (El "cerebro" de Django)

Este hook es ahora mucho más inteligente y se encarga de todo el ciclo de vida de Django.

cd ~/repos/mi_proyecto.git/hooks
nano post-receive

Pega este contenido:

#!/bin/bash

# --- Configuración ---
TARGET="/home/deploy/mi_proyecto_live" # Carpeta de archivos "vivos"
GIT_DIR="/home/deploy/repos/mi_proyecto.git"
VENV_DIR="/home/deploy/venvs/mi_proyecto_venv" # Carpeta para el entorno virtual
BRANCH="main"
SERVICE_NAME="gunicorn_mi_proyecto" # Nombre de nuestro servicio systemd

while read oldrev newrev ref
do
    # Revisar si la rama es la que queremos (ej. "main")
    if [[ $ref = refs/heads/$BRANCH ]];
    then
        echo "Deploying $BRANCH branch..."
        
        # 1. Hacer checkout de los archivos
        mkdir -p $TARGET
        git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
        
        # --- MAGIA DE PYTHON/DJANGO ---
        
        # 2. Crear Venv si no existe
        if [ ! -d "$VENV_DIR" ]; then
            echo "Creating Python virtual environment at $VENV_DIR..."
            python3 -m venv $VENV_DIR
        fi
        
        # 3. Activar venv e instalar dependencias
        echo "Activating venv and installing requirements..."
        source $VENV_DIR/bin/activate
        # Asegurate de que 'gunicorn' está en tu requirements.txt
        pip install -r $TARGET/requirements.txt
        
        # 4. Correr comandos de Django (desde la carpeta del proyecto)
        echo "Running Django management commands..."
        cd $TARGET
        python manage.py migrate --noinput
        python manage.py collectstatic --noinput # Recolecta estáticos
        
        # 5. Reiniciar el servicio Gunicorn
        # (Este servicio lo crearemos en el Paso 6)
        echo "Restarting Gunicorn service: $SERVICE_NAME..."
        sudo systemctl restart $SERVICE_NAME
        
        echo "Deploy complete."
    else
        echo "Push received for branch $ref, no deploy action taken."
    fi
done

Dar permisos y crear carpetas:

chmod +x post-receive
mkdir -p /home/deploy/mi_proyecto_live
mkdir -p /home/deploy/venvs/mi_proyecto_venv

🚀 5. Hacer deploy desde tu máquina local

(En tu máquina local, en la carpeta de tu proyecto)

¡Importante! Antes de hacer push, asegurate de que tu proyecto Django esté listo para producción:

  1. Añadí gunicorn a tu archivo requirements.txt.
  2. En settings.py:
    • DEBUG = False
    • ALLOWED_HOSTS = ['tu-dominio.com', 'ip_vps']
    • STATIC_ROOT = BASE_DIR / 'staticfiles' (o os.path.join(BASE_DIR, 'staticfiles'))

Añadí el remoto y hacé push:

git remote add production deploy@ip_vps:~/repos/mi_proyecto.git
git push production main

El primer deploy va a fallar al final (al intentar reiniciar Gunicorn), porque aún no hemos creado el servicio. ¡No te preocupés! Los archivos ya están en el servidor, que es lo que necesitábamos.


🌐 6. Configurar Gunicorn y Nginx (Opción B: Django)

Ahora conectaremos todo. Usaremos systemd (el gestor de servicios de Linux) para manejar Gunicorn.

(Conectado como deploy@ip_vps)

A. Crear el servicio de Gunicorn

Gunicorn será el "trabajador" que ejecuta tu código Python.

sudo nano /etc/systemd/system/gunicorn_mi_proyecto.service

Pegá esta configuración (ajustá mi_proyecto.wsgi:application al nombre de tu proyecto):

[Unit]
Description=gunicorn daemon for mi_proyecto
After=network.target

[Service]
User=deploy
Group=www-data
WorkingDirectory=/home/deploy/mi_proyecto_live
ExecStart=/home/deploy/venvs/mi_proyecto_venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn_mi_proyecto.sock \
          mi_proyecto.wsgi:application

[Install]
WantedBy=multi-user.target
  • WorkingDirectory: Dónde vive tu manage.py.
  • ExecStart: Apunta al ejecutable de gunicorn dentro del venv y le dice que cree un "socket" (un archivo de comunicación) en /run/gunicorn_mi_proyecto.sock.
  • mi_proyecto.wsgi:application: Es el "punto de entrada" de tu app. Se encuentra en mi_proyecto/wsgi.py.

B. Activar el servicio Gunicorn

sudo systemctl start gunicorn_mi_proyecto
sudo systemctl enable gunicorn_mi_proyecto

Comprobá que esté corriendo: sudo systemctl status gunicorn_mi_proyecto (Presioná q para salir).

C. Configurar Nginx como Reverse Proxy

Nginx será el "recepcionista". Le pedirá a Gunicorn el contenido dinámico y servirá los archivos estáticos (CSS/JS) él mismo.

sudo nano /etc/nginx/sites-available/mi_proyecto

Pegá esta configuración:

server {
    listen 80;
    server_name tu-dominio.com www.tu-dominio.com; # O tu IP

    # Ubicación del socket de Gunicorn
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn_mi_proyecto.sock;
    }

    # ¡IMPORTANTE! Servir archivos estáticos
    # Esta ruta debe coincidir con tu STATIC_ROOT
    location /static/ {
        root /home/deploy/mi_proyecto_live;
    }

    # (Opcional) Servir archivos subidos por usuarios (media)
    # location /media/ {
    #     root /home/deploy/mi_proyecto_live;
    # }
}

D. Activar el sitio y recargar Nginx

sudo ln -s /etc/nginx/sites-available/mi_proyecto /etc/nginx/sites-enabled/
# Quitamos el sitio por defecto si existe
sudo rm /etc/nginx/sites-enabled/default 
sudo nginx -t
sudo systemctl reload nginx

¡Visitá tu IP o dominio! Deberías ver tu app Django.


🚀 7. ¡Deploy final!

Ahora que todo está configurado, hacé un pequeño cambio en tu código local (ej. en un README.md), y volvé a hacer push:

(En tu máquina local)

git commit -m "Probando deploy automático"
git push production main

Observá la terminal. Esta vez, el hook post-receive se ejecutará por completo, instalará dependencias, correrá collectstatic y migrate, y reiniciará Gunicorn sin errores. Tu cambio aparecerá en vivo en segundos.

🛠️ Alternativa: Script de deploy manual

Si preferís hacer deploy manualmente (sin Git hooks), podés usar este script en el servidor:

#!/bin/bash
# deploy.sh - Helper para deploy manual

echo "🚀 Iniciando deploy..."

cd /home/deploy/mi_proyecto_live

echo "📥 Descargando cambios..."
git pull origin main

echo "🐍 Activando entorno virtual..."
source /home/deploy/venvs/mi_proyecto_venv/bin/activate

echo "📦 Instalando dependencias..."
pip install -r requirements.txt

echo "🗄️  Migrando base de datos..."
python manage.py migrate --noinput

echo "📁 Recolectando archivos estáticos..."
python manage.py collectstatic --noinput

echo "🔄 Reiniciando Gunicorn..."
sudo systemctl restart gunicorn_mi_proyecto

echo "✅ Deploy completado!"

Uso: Corre ./deploy.sh cuando quieras deployar manualmente. Yo lo uso cuando algo falló con el hook y necesito debuggear.


🔒 8. ¡Paso Final! HTTPS con Let's Encrypt

(Esto es igual que antes, pero vale la pena repetirlo)

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d tu-dominio.com -d www.tu-dominio.com

Seguí las instrucciones y Certbot configurará automáticamente Nginx para usar SSL.


🧠 4. Buenas prácticas

Cosas que aprendí a las piñas:

  • Hacé commits chicos y con mensajes claros. "fix" no es un mensaje de commit.
  • NUNCA, NUNCA subas .env o API keys a Git. Usá .gitignore religiosamente.
  • Variables de entorno: usá archivos .env en el servidor (fuera de Git) y cargalos con python-decouple o similar.
  • Hacé backups. En serio. Configurá snapshots automáticos en tu proveedor de VPS.
  • Este setup con Git hooks está genial para proyectos personales. Para cosas más grandes, mirá GitHub Actions o GitLab CI.

🏁 Resultado final

Si llegaste hasta acá, felicitaciones. Ahora tenés:

  • Conocimientos sólidos de Linux (ya no le tenés miedo a la terminal)
  • Git bajo control (branches, merges, conflictos, todo)
  • Tu proyecto corriendo en un VPS real, con HTTPS
  • Deploy automático con git push (esto es lo más satisfactorio)
  • Independencia de plataformas cerradas

Ahora podés deployar lo que quieras, cuando quieras. Esa sensación no tiene precio.


📎 Apéndice: Alternativas sin VPS (ngrok y Cloudflare Tunnels)

Si no querés o no podés pagar un VPS todavía, hay alternativas para exponer tu proyecto local a internet. Son geniales para demos, testing, o webhooks.

🚇 ngrok

ngrok crea un túnel seguro desde internet a tu localhost. Es súper fácil de usar.

Instalación:

# Mac (con Homebrew)
brew install ngrok

# Linux
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
  sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \
  echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | \
  sudo tee /etc/apt/sources.list.d/ngrok.list && \
  sudo apt update && sudo apt install ngrok

Uso básico:

# Registrate en https://ngrok.com y obtené tu authtoken
ngrok config add-authtoken TU_TOKEN

# Exponer un servidor local en el puerto 8000
ngrok http 8000

Te va a dar una URL tipo https://abc123.ngrok.io que apunta a tu localhost:8000.

Ejemplo con Django:

# Terminal 1: Corré tu servidor Django
python manage.py runserver

# Terminal 2: Exponelo con ngrok
ngrok http 8000

Pros:

  • Setup en 2 minutos
  • HTTPS gratis
  • Perfecto para demos y webhooks
  • Plan gratuito disponible

Contras:

  • La URL cambia cada vez (en el plan free)
  • No es para producción
  • Límites en el plan gratuito

☁️ Cloudflare Tunnels (cloudflared)

Cloudflare Tunnels es similar a ngrok pero con más features y sin límites de ancho de banda.

Instalación:

# Mac
brew install cloudflared

# Linux (Ubuntu/Debian)
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

Setup rápido (sin dominio):

# Exponer localhost:8000
cloudflared tunnel --url http://localhost:8000

Te da una URL tipo https://random-words.trycloudflare.com.

Setup con dominio propio:

Si tenés un dominio en Cloudflare, podés crear un túnel permanente:

# 1. Login
cloudflared tunnel login

# 2. Crear túnel
cloudflared tunnel create mi-proyecto

# 3. Configurar DNS
cloudflared tunnel route dns mi-proyecto app.tudominio.com

# 4. Crear config
nano ~/.cloudflared/config.yml

Contenido de config.yml:

tunnel: mi-proyecto
credentials-file: /home/user/.cloudflared/TUNNEL_ID.json

ingress:
  - hostname: app.tudominio.com
    service: http://localhost:8000
  - service: http_status:404

Correr el túnel:

cloudflared tunnel run mi-proyecto

Pros:

  • Sin límites de ancho de banda
  • Podés usar tu propio dominio
  • Más estable que ngrok
  • Gratis

Contras:

  • Setup inicial un poco más complejo
  • Necesitás Cloudflare para usar dominio propio

🤔 ¿Cuándo usar cada uno?

ngrok:

  • Demos rápidas
  • Testing de webhooks (Stripe, GitHub, etc.)
  • Mostrarle algo a un cliente
  • No querés configurar nada

Cloudflare Tunnels:

  • Proyectos más permanentes
  • Tenés dominio en Cloudflare
  • Necesitás más ancho de banda
  • Querés algo más profesional

VPS (lo que vimos en el taller):

  • Producción real
  • Control total
  • Aprender DevOps
  • Proyectos serios

Nota: ngrok y Cloudflare Tunnels son geniales para desarrollo y demos, pero para producción real, un VPS te da mucho más control y aprendizaje.

🔐 Seguridad con túneles

Algunos tips si usás túneles:

# Django settings.py
# Agregá el dominio de ngrok/cloudflare a ALLOWED_HOSTS
ALLOWED_HOSTS = [
    'localhost',
    '127.0.0.1',
    'abc123.ngrok.io',  # Tu URL de ngrok
    'app.tudominio.com',  # Tu dominio de Cloudflare
]

# Nunca dejes DEBUG=True en producción
DEBUG = False

Protección básica con contraseña (opcional):

# Con ngrok, podés agregar autenticación básica
ngrok http 8000 --basic-auth="usuario:password"

📚 Recursos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment