--- title: "Cómo desplegar Django en Ubuntu 24.04 con Gunicorn y Nginx" description: "Despliega Django en Ubuntu 24.04 con PostgreSQL, Gunicorn gestionado por systemd y Nginx como proxy inverso, con HTTPS de Let's Encrypt y ajustes seguros." category: "E-commerce y CMS" language: es source: https://cubepath.com/es/docs/e-commerce-y-cms/implementacion-de-aplicacion-django-en-linux updated: 2026-09-25 --- # Cómo desplegar Django en Ubuntu 24.04 con Gunicorn y Nginx Django incluye un servidor de desarrollo que no está pensado para producción. Para publicar un proyecto se usa un servidor WSGI como Gunicorn, que ejecuta el código Python, y Nginx delante para atender las conexiones, servir los archivos estáticos y gestionar TLS. En este tutorial desplegarás un proyecto Django en Ubuntu 24.04 con PostgreSQL, Gunicorn activado por socket de systemd, Nginx y un certificado de Let's Encrypt. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM. - Un usuario no root con privilegios `sudo`. En los ejemplos se llama `your_user`. - Un dominio con un registro DNS `A` apuntando a la IP del servidor. En los ejemplos se usa `your_domain`. El proyecto de ejemplo se llama `myproject` y vive en `/var/www/myproject`. Si despliegas tu propio código, sustituye el nombre del paquete de configuración (el directorio que contiene `settings.py` y `wsgi.py`) donde aparezca `myproject`. ## Paso 1: Instalar los paquetes del sistema Ubuntu 24.04 trae Python 3.12, compatible con las versiones actuales de Django. Instala el módulo de entornos virtuales, PostgreSQL y Nginx: ```bash sudo apt update sudo apt install python3-venv python3-dev postgresql nginx git ``` Comprueba que PostgreSQL está en marcha: ```bash systemctl status postgresql --no-pager ``` ```output ● postgresql.service - PostgreSQL RDBMS Active: active (exited) since ... ``` El estado `active (exited)` es normal: ese servicio solo agrupa los clústeres, que se ejecutan en `postgresql@16-main`. Abre en el cortafuegos SSH, HTTP y HTTPS: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` ## Paso 2: Crear la base de datos PostgreSQL Crea un usuario de PostgreSQL y una base de datos que le pertenezca. Desde PostgreSQL 15 solo el propietario de la base de datos puede crear tablas en el esquema `public`, así que usar `OWNER` evita errores de permisos al migrar. Sustituye `your_strong_password` por una contraseña larga y aleatoria: ```bash sudo -u postgres psql -c "CREATE USER myproject WITH PASSWORD 'your_strong_password';" sudo -u postgres psql -c "CREATE DATABASE myproject OWNER myproject;" ``` Comprueba que puedes conectarte con el nuevo usuario: ```bash psql -h localhost -U myproject -d myproject -c "SELECT current_user;" ``` ```output current_user -------------- myproject (1 row) ``` ## Paso 3: Preparar el proyecto y el entorno virtual Crea el directorio de la aplicación con tu usuario como propietario. Se usa `/var/www` y no tu directorio personal porque en Ubuntu 24.04 los directorios de `/home` tienen permisos `750` y Nginx no podría leer los archivos estáticos: ```bash sudo mkdir -p /var/www/myproject sudo chown your_user:your_user /var/www/myproject cd /var/www/myproject ``` Si tienes el proyecto en un repositorio, clónalo en ese directorio con `git clone https://github.com/your_account/myproject.git .`. Crea un entorno virtual para aislar las dependencias del Python del sistema y actívalo: ```bash python3 -m venv venv source venv/bin/activate ``` Instala Django, Gunicorn y el controlador de PostgreSQL. `psycopg[binary]` incluye la librería de cliente, así que no necesitas compilar nada: ```bash pip install --upgrade pip pip install django gunicorn "psycopg[binary]" ``` Si clonaste tu proyecto, instala además sus dependencias con `pip install -r requirements.txt`. Si solo quieres probar el despliegue, crea un proyecto nuevo: ```bash django-admin startproject myproject . ``` Comprueba la versión instalada: ```bash python -m django --version ``` ```output 5.2.x ``` ## Paso 4: Guardar los secretos en un archivo de entorno La clave secreta y la contraseña de la base de datos no deben estar en el código. Genera una clave aleatoria: ```bash python3 -c "import secrets; print(secrets.token_urlsafe(50))" ``` Crea el archivo de entorno que leerán Gunicorn y tus comandos de gestión: ```bash nano /var/www/myproject/.env ``` ```ini DJANGO_SECRET_KEY=la_clave_generada DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=your_domain,www.your_domain DB_NAME=myproject DB_USER=myproject DB_PASSWORD=your_strong_password ``` Restringe los permisos para que solo tu usuario pueda leerlo: ```bash chmod 600 /var/www/myproject/.env ``` ## Paso 5: Configurar Django para producción Abre el archivo de ajustes: ```bash nano /var/www/myproject/myproject/settings.py ``` Añade `import os` al principio del archivo y sustituye las líneas de `SECRET_KEY`, `DEBUG`, `ALLOWED_HOSTS` y el bloque `DATABASES` por estas: ```python SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] DEBUG = os.environ.get("DJANGO_DEBUG", "False") == "True" ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["DB_NAME"], "USER": os.environ["DB_USER"], "PASSWORD": os.environ["DB_PASSWORD"], "HOST": "localhost", "PORT": "5432", } } ``` Al final del archivo, define dónde se reunirán los archivos estáticos e indica a Django que confíe en la cabecera con la que Nginx le dirá si la petición original llegó por HTTPS: ```python STATIC_ROOT = BASE_DIR / "staticfiles" SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True ``` Carga las variables de entorno en tu sesión para ejecutar los comandos de gestión: ```bash cd /var/www/myproject set -a; source .env; set +a ``` Aplica las migraciones, crea un superusuario para el panel de administración y copia los archivos estáticos a `STATIC_ROOT`: ```bash python manage.py migrate python manage.py createsuperuser python manage.py collectstatic --noinput ``` ```output Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK ... 127 static files copied to '/var/www/myproject/staticfiles'. ``` Por último, ejecuta la comprobación de despliegue de Django, que avisa de ajustes inseguros: ```bash python manage.py check --deploy ``` Es normal que aparezcan avisos sobre `SECURE_HSTS_SECONDS` y `SECURE_SSL_REDIRECT`: la redirección a HTTPS la hará Nginx, y HSTS conviene activarlo solo cuando HTTPS funcione de forma estable. Sal del entorno virtual con `deactivate`. ## Paso 6: Ejecutar Gunicorn con systemd Usarás dos unidades: un socket que escucha en `/run/gunicorn.sock` y un servicio que arranca Gunicorn cuando llega la primera conexión. Crea el socket: ```bash sudo nano /etc/systemd/system/gunicorn.socket ``` ```ini [Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target ``` Crea el servicio. `EnvironmentFile` carga el archivo `.env` y `--workers 3` es un buen punto de partida para un servidor de 1 o 2 vCPU (la regla habitual es `2 x núcleos + 1`): ```bash sudo nano /etc/systemd/system/gunicorn.service ``` ```ini [Unit] Description=gunicorn daemon for myproject Requires=gunicorn.socket After=network.target [Service] User=your_user Group=www-data WorkingDirectory=/var/www/myproject EnvironmentFile=/var/www/myproject/.env ExecStart=/var/www/myproject/venv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ myproject.wsgi:application Restart=on-failure [Install] WantedBy=multi-user.target ``` Activa e inicia el socket: ```bash sudo systemctl daemon-reload sudo systemctl enable --now gunicorn.socket ``` Envía una petición al socket para que systemd arranque el servicio. La cabecera `Host` debe estar en `ALLOWED_HOSTS`: ```bash curl -s -o /dev/null -w "%{http_code}\n" --unix-socket /run/gunicorn.sock -H "Host: your_domain" http://localhost/admin/login/ ``` ```output 200 ``` Comprueba que el servicio se ha iniciado: ```bash systemctl status gunicorn --no-pager ``` ```output ● gunicorn.service - gunicorn daemon for myproject Active: active (running) since ... ``` Si algo falla, el error de Python aparece en `sudo journalctl -u gunicorn`. ## Paso 7: Configurar Nginx como proxy inverso Crea un bloque de servidor para el dominio: ```bash sudo nano /etc/nginx/sites-available/myproject ``` Nginx servirá directamente `/static/` desde disco y enviará el resto de peticiones a Gunicorn. El archivo `proxy_params` de Ubuntu añade las cabeceras `Host`, `X-Real-IP`, `X-Forwarded-For` y `X-Forwarded-Proto`: ```nginx server { listen 80; listen [::]:80; server_name your_domain www.your_domain; client_max_body_size 20M; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { alias /var/www/myproject/staticfiles/; expires 30d; } location / { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } } ``` Activa el sitio, desactiva el sitio por defecto y comprueba la sintaxis: ```bash sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx ``` ## Paso 8: Activar HTTPS con Let's Encrypt Instala Certbot con su plugin para Nginx y solicita el certificado. Certbot añade la configuración TLS y la redirección de HTTP a HTTPS: ```bash sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your_domain -d www.your_domain ``` Comprueba que la renovación automática funciona: ```bash sudo certbot renew --dry-run ``` Abre `https://your_domain/admin/` en el navegador. Deberías ver el formulario de acceso del panel de administración con sus estilos cargados, lo que confirma que Nginx sirve los estáticos. Entra con el superusuario que creaste. > Nota: como las cookies de sesión son `Secure`, el acceso al panel solo funciona por HTTPS. Si intentas iniciar sesión por HTTP antes de este paso, el formulario fallará con un error CSRF. ## Actualizar la aplicación Para desplegar cambios, descarga el código, instala dependencias, migra, reúne los estáticos y reinicia Gunicorn: ```bash cd /var/www/myproject git pull source venv/bin/activate set -a; source .env; set +a pip install -r requirements.txt python manage.py migrate python manage.py collectstatic --noinput deactivate sudo systemctl restart gunicorn ``` ## Solución de problemas - **502 Bad Gateway**: Gunicorn no arranca. Revisa `sudo journalctl -u gunicorn -n 50`; los fallos típicos son una variable de `.env` que falta (`KeyError`) o un módulo WSGI mal escrito en `ExecStart`. - **400 Bad Request**: el dominio no está en `DJANGO_ALLOWED_HOSTS`. Corrige `.env` y ejecuta `sudo systemctl restart gunicorn`. - **El panel se ve sin estilos**: no has ejecutado `collectstatic` o la ruta de `alias` no coincide con `STATIC_ROOT`. Comprueba que existe `/var/www/myproject/staticfiles/admin/`. - **Cambios en las unidades de systemd sin efecto**: tras editar los archivos ejecuta `sudo systemctl daemon-reload` y `sudo systemctl restart gunicorn.socket gunicorn.service`. ## Conclusión Tu proyecto Django funciona en producción con PostgreSQL, Gunicorn gestionado por systemd y Nginx sirviendo los estáticos y el tráfico HTTPS. Como siguientes pasos puedes: - Activar `SECURE_HSTS_SECONDS` cuando HTTPS lleve un tiempo funcionando sin problemas. - Configurar almacenamiento para los archivos subidos (`MEDIA_ROOT`) y servirlos con otro bloque `location` en Nginx. - Añadir Celery con Redis si la aplicación necesita tareas en segundo plano.