Ubuntu 22.04: installazione pulita di Django con Python, virtualenv, Gunicorn e Nginx
Su Ubuntu 22.04 la strada più sensata per Django non è installarlo “globale” nel sistema, ma isolarlo in un ambiente virtuale, esporlo con Gunicorn e metterlo dietro Nginx. Così separi dipendenze, riduci il rischio di conflitti con i pacchetti di sistema e hai un layout che regge bene anche quando il progetto cresce.
Qui sotto trovi una procedura completa, pensata per un server appena installato o comunque pulito. L’obiettivo è arrivare a un progetto Django funzionante, servito in produzione in modo semplice e verificabile, senza saltare i passaggi che di solito creano problemi: versioni Python, permessi, socket, statici e configurazione del reverse proxy.
1. Pacchetti base e prerequisiti
Prima di toccare Django conviene aggiornare il sistema e installare i pacchetti necessari per Python, compilazione di eventuali dipendenze e Nginx. Su Ubuntu 22.04 Python 3 è già presente, ma i moduli per creare ambienti virtuali spesso non lo sono in un’installazione minimale.
sudo apt update
sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip python3-dev build-essential nginx
Dopo l’installazione, verifica almeno le versioni principali. Non serve inseguire la versione più nuova di Django in assoluto: quello che conta è avere un Python supportato e una base coerente con il progetto.
python3 --version
nginx -v
Atteso: Python 3.10 su Ubuntu 22.04 e Nginx installato senza errori. Se `python3-venv` manca, il comando di creazione dell’ambiente virtuale fallisce subito con un errore abbastanza esplicito; in quel caso non andare avanti a tentativi, installa il pacchetto e riparti.
2. Creare utente e directory del progetto
Per una macchina di produzione non conviene lavorare come root. Crea un utente dedicato al progetto o usa un account di servizio con permessi limitati, poi prepara una directory sotto `/srv` o in una posizione equivalente che renda chiaro cosa appartiene all’applicazione.
sudo adduser djangoapp
sudo mkdir -p /srv/djangoapp
sudo chown -R djangoapp:djangoapp /srv/djangoapp
Se preferisci mantenere il codice sotto la home dell’utente, va bene lo stesso; l’importante è che la scelta sia stabile e che i permessi non lascino il progetto scrivibile da account non necessari. In ambiente hosting o multiutente, questo punto evita parecchi incidenti banali ma costosi.
3. Ambiente virtuale e installazione di Django
L’ambiente virtuale è il punto chiave. Qui installi Django e le dipendenze del progetto senza sporcare il sistema. È una scelta tecnica semplice, ma è anche quella che rende più facile aggiornare o fare rollback più avanti.
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && python3 -m venv venv'
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/pip install --upgrade pip'
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/pip install django gunicorn'
Controlla che il virtualenv esista e che i binari siano nel posto giusto.
ls -l /srv/djangoapp/venv/bin/
/srv/djangoapp/venv/bin/python --version
/srv/djangoapp/venv/bin/pip show django gunicorn
Se `pip show` non restituisce i pacchetti, significa che l’installazione non è andata a buon fine oppure che stai usando un interprete diverso da quello del virtualenv. In quel caso il problema non è Django: è il contesto Python.
4. Creazione del progetto Django
Con l’ambiente pronto puoi creare il progetto. Qui uso un nome generico, ma in un caso reale conviene evitare collisioni con nomi di moduli o directory già presenti.
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/django-admin startproject config .'
Il comando crea il classico `manage.py` nella root del progetto e la directory `config` con le impostazioni. Il punto da verificare subito è che la struttura sia coerente e che l’utente del servizio possa leggere tutto.
find /srv/djangoapp -maxdepth 2 -type f | sort
A questo punto fai una prima migrazione e una prova con il server di sviluppo, solo per verificare che Django parta. Non è il modo in cui andrai in produzione, ma è il test più rapido per distinguere un problema dell’app da un problema del proxy.
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/python manage.py migrate'
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/python manage.py runserver 0.0.0.0:8000'
Apri poi `http://IP_DEL_SERVER:8000/` da una macchina autorizzata. Se vedi la pagina iniziale di Django, il livello applicativo è sano. Se il browser non raggiunge il server, il problema è di rete o firewall, non del framework.
5. Impostazioni di base del progetto
Prima di esporre l’app dietro Nginx, sistema alcune impostazioni fondamentali. Le più importanti sono `ALLOWED_HOSTS`, la gestione dei file statici e la chiave segreta. La chiave segreta non va mai messa in chiaro in un repository pubblico; se la stai già usando male, ruotala e spostala fuori dal codice.
Modifica `config/settings.py` e imposta almeno questi valori. L’esempio è minimale, ma sufficiente per un primo deploy controllato.
ALLOWED_HOSTS = ['tuo-dominio.it', 'www.tuo-dominio.it', 'IP_DEL_SERVER']
STATIC_URL = '/static/'
STATIC_ROOT = '/srv/djangoapp/static/'
DEBUG = False
Se usi variabili d’ambiente per `SECRET_KEY`, `DEBUG` o credenziali del database, meglio ancora. In produzione la configurazione deve essere esterna al codice quando possibile, così il deploy non trasforma ogni modifica in una riscrittura del progetto.
6. Raccolta dei file statici
Nginx deve servire i file statici direttamente, non passarli a Gunicorn. Per questo Django deve copiarli in una directory dedicata tramite `collectstatic`.
sudo mkdir -p /srv/djangoapp/static
sudo chown -R djangoapp:djangoapp /srv/djangoapp/static
sudo -u djangoapp bash -lc 'cd /srv/djangoapp && ./venv/bin/python manage.py collectstatic'
Il comando ti chiede conferma se `STATIC_ROOT` è configurato correttamente. Dopo l’esecuzione verifica che la directory contenga i file attesi.
find /srv/djangoapp/static -type f | head
Se `collectstatic` fallisce, di solito il motivo è banale: `STATIC_ROOT` non esiste, permessi errati o un path scritto male. Non è un problema di Nginx, quindi conviene correggere prima il lato Django.
7. Gunicorn come servizio systemd
Per tenere Django sempre attivo, crea un servizio systemd per Gunicorn. È una soluzione pulita, leggibile e facile da controllare con gli strumenti standard di Ubuntu.
Crea il file `/etc/systemd/system/djangoapp.service` con una configurazione simile a questa.
[Unit]
Description=Gunicorn for Django app
After=network.target
[Service]
User=djangoapp
Group=www-data
WorkingDirectory=/srv/djangoapp
Environment="PATH=/srv/djangoapp/venv/bin"
ExecStart=/srv/djangoapp/venv/bin/gunicorn --workers 3 --bind unix:/run/djangoapp.sock config.wsgi:application
[Install]
WantedBy=multi-user.target
Dopo aver salvato il file, ricarica systemd e avvia il servizio.
sudo systemctl daemon-reload
sudo systemctl enable --now djangoapp.service
sudo systemctl status djangoapp.service
Il controllo utile non è solo “active (running)”: guarda anche se il socket Unix è stato creato e se Gunicorn ascolta davvero dove previsto.
ls -l /run/djangoapp.sock
journalctl -u djangoapp.service -n 50 --no-pager
Se il servizio non parte, le cause frequenti sono tre: path sbagliato al virtualenv, modulo WSGI non trovato oppure permessi sul socket. In pratica, il log di systemd ti dice quasi sempre dove guardare.
8. Nginx come reverse proxy
Con Gunicorn attivo, Nginx si occupa di ricevere le richieste HTTP, servire i file statici e inoltrare il resto all’applicazione. È il punto in cui separi bene il traffico pubblico dall’esecuzione Python.
Crea ad esempio `/etc/nginx/sites-available/djangoapp` con una configurazione essenziale ma corretta.
server {
listen 80;
server_name tuo-dominio.it www.tuo-dominio.it;
location /static/ {
alias /srv/djangoapp/static/;
}
location / {
proxy_pass http://unix:/run/djangoapp.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Abilita il sito, testa la sintassi e ricarica Nginx.
sudo ln -s /etc/nginx/sites-available/djangoapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Se `nginx -t` fallisce, non ricaricare: correggi prima l’errore indicato. Se invece Nginx risponde ma Django no, controlla il socket e il servizio Gunicorn; se il socket esiste ma il proxy restituisce 502, il problema è quasi sempre sul backend.
9. Verifica end-to-end e diagnostica rapida
A questo punto fai una verifica completa dal bordo fino all’applicazione. Non limitarti a un refresh del browser: guarda lo status HTTP, i log e il comportamento del socket.
curl -I http://tuo-dominio.it/
curl -I http://127.0.0.1/
sudo tail -n 50 /var/log/nginx/error.log
journalctl -u djangoapp.service -n 50 --no-pager
Atteso: risposta `200` o comunque una risposta valida dall’app, nessun errore di connessione al socket in Nginx e nessun traceback in Gunicorn. Se compare una pagina bianca o un `500`, guarda prima i log dell’applicazione, poi quelli del proxy. In molti casi il problema è un `ALLOWED_HOSTS` incompleto o un errore nel file `settings.py`.
Un controllo utile è anche la catena di permessi sul socket: `djangoapp` crea il socket, `www-data` deve poterlo leggere/scrivere tramite il gruppo. Se hai cambiato utenti o gruppi, non dare per scontato che il servizio resti funzionante dopo il primo reboot.
10. HTTPS con Certbot
Una volta verificato il traffico in chiaro, passa a TLS. Su Ubuntu 22.04 la via più lineare è Certbot con il plugin per Nginx. Non è un vezzo estetico: senza HTTPS esponi sessioni, cookie e credenziali a intercettazione o downgrade banali.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d tuo-dominio.it -d www.tuo-dominio.it
Alla fine verifica il rinnovo automatico e la presenza del timer di systemd.
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
Se il certificato non viene emesso, le cause tipiche sono DNS non ancora propagato, porta 80 bloccata o `server_name` errato. Qui la diagnostica è semplice: il validatore ACME deve raggiungere il vhost corretto su HTTP prima di poter completare la challenge.
11. Prime estensioni utili dopo l’installazione
Una installazione funzionante non è ancora un’installazione pronta per crescere. I passi successivi dipendono dal tipo di progetto, ma ci sono alcune estensioni tipiche che hanno senso quasi sempre: database vero, variabili d’ambiente, logging ordinato e gestione dei backup.
Se il sito usa SQLite solo per sviluppo, passa a PostgreSQL o MySQL/MariaDB appena il progetto esce dal test. Se resti su SQLite in produzione, fallo solo per carichi piccoli e con consapevolezza dei limiti sul locking. Per progetti web reali, il database è spesso il punto che determina affidabilità e manutenzione più del framework stesso.
Anche il logging merita attenzione: i traceback devono finire nei log di systemd o in file dedicati, non dispersi nel vuoto. Un controllo minimo utile è verificare che gli errori applicativi siano leggibili e che il livello di verbosità non esponga dati sensibili.
12. Schema mentale per non perdersi quando qualcosa si rompe
Quando Django non risponde, conviene ragionare per layer: DNS, Nginx, Gunicorn, applicazione, database, storage. Questo evita di passare mezz’ora a cambiare file a caso. Se il dominio risolve ma Nginx risponde `502`, il guasto è quasi certamente tra reverse proxy e backend. Se Nginx risponde `500`, il problema è più spesso lato app o configurazione Django. Se il socket non esiste, il servizio non è partito o è morto subito dopo l’avvio.
La regola pratica è semplice: prima osserva, poi modifica. Un `curl -I`, un `systemctl status`, un `journalctl` e un `nginx -t` valgono più di un riavvio cieco. Su installazioni pulite di Django, il 90% dei problemi iniziali si chiude in quei quattro controlli.
Checklist finale di installazione
Prima di considerare conclusa l’installazione, verifica questi punti in sequenza:
- Il virtualenv esiste in `/srv/djangoapp/venv` e contiene Django e Gunicorn.
- `manage.py migrate` termina senza errori.
- `collectstatic` popola `/srv/djangoapp/static/`.
- `systemctl status djangoapp.service` mostra il servizio attivo.
- `nginx -t` passa senza errori di sintassi.
- `curl -I http://tuo-dominio.it/` restituisce una risposta valida.
- Certbot ha emesso il certificato e `certbot renew --dry-run` va a buon fine.
Se tutti i punti sono verdi, hai una base corretta per lavorare su app, database e deployment continuo. Se uno solo di questi salta, non forzare il passo successivo: in questa catena il guasto si propaga facilmente e il tempo perso a inseguire sintomi è sempre maggiore del tempo speso a chiudere il primo errore pulito.
Commenti (0)
Nessun commento ancora.
Segnala contenuto
Elimina commento
Eliminare definitivamente questo commento?
L'azione non si può annullare.