Como publicar Django na VPS com Gunicorn, Docker e HTTPS

Fala aí, beleza? Se tu quer colocar um projeto Django no ar numa VPS do jeito certo, a resposta curta é esta: o Gunicorn roda a aplicação como servidor WSGI (comando base gunicorn myproject.wsgi), o Nginx fica na frente como proxy reverso cuidando do HTTPS e dos arquivos estáticos, e tudo isso vai empacotado em containers Docker
Tenha acesso root e SSD NVMe, com 10% OFF extra pelo link da Hora de Codar.
Ver a oferta de VPS Parceria Hora de CodarE nada de runserver em produção, combinado? Desde o Django 5.2 o próprio comando avisa que é um servidor de desenvolvimento e que não deve ser usado em produção 😛
O cenário aqui é um projeto em Django 6.1, a versão estável atual (lançada em 5 de agosto de 2026), com o Gunicorn na versão mais recente disponível (a 26.2.2, de 6 de setembro de 2026), cuja página oficial de instalação pede Python 3.12 ou superior
A espinha do deploy é sempre a mesma: DEBUG desligado, ALLOWED_HOSTS certinho, SECRET_KEY fora do git, estáticos com collectstatic, migrate aplicado e proxy confiável
Ainda está no Django 6.0? Se liga: ele saiu do suporte principal com a chegada do 6.1, o último bugfix foi o 6.0.8 (que também foi uma versão de segurança), e agora ele só recebe correções de segurança e de perda de dados até abril de 2027…
O que você precisa antes de publicar o Django na VPS
Antes de abrir o terminal, confere se o básico está no lugar, porque é aqui que muito deploy trava:
- Python 3.12 ou superior, que é o que a página oficial de instalação do Gunicorn exige hoje (e a versão também precisa ser suportada pelo Django que tu usa)
- Projeto Django com
manage.pye o módulowsgi(omyproject/wsgi.pyque o próprio Django gera) - Migrations já criadas no desenvolvimento e versionadas no git, junto com o código
- Domínio apontado para o IP da VPS, porque o HTTPS e o ALLOWED_HOSTS dependem dele
- Uma VPS com acesso root, já que tu vai instalar e controlar Docker e Nginx por conta própria
Pra esse projeto, a recomendação principal da Hora de Codar é a VPS da Hostinger (este artigo contém links de afiliado da Hostinger)
O motivo é bem prático: a VPS vem com acesso root completo, o que é obrigatório pra rodar Docker e Nginx do seu jeito, e existe um template com Ubuntu 24.04 que já traz docker-ce e docker-compose instalados, aí tu pula a parte chata da instalação
Também tem terminal SSH direto no navegador com acesso root, armazenamento SSD NVMe, processadores AMD EPYC, proteção contra DDoS e opção de servidor no Brasil, o que ajuda quando o seu público está por aqui 🙂
E qual plano pegar? A linha KVM vai do KVM 1 (1 vCPU, 4 GB de RAM, 50 GB NVMe) até o KVM 8 (8 vCPU, 32 GB de RAM, 400 GB NVMe)
O número de vCPUs conversa direto com o Gunicorn: a orientação dele usa como ponto de partida (2 x núcleos) + 1 workers, então no KVM 1 isso dá 3 workers e no KVM 8 dá 17
Passo 1: ajuste os settings do Django para produção
O primeiro passo é fazer o Django ler as configurações sensíveis do ambiente, com DEBUG desligado e os hosts declarados
Por que tanto cuidado com o DEBUG? Porque ligado em produção ele expõe trechos do código-fonte, variáveis locais, settings e as bibliotecas que tu usa, tudo de bandeja pra quem provocar um erro na sua página
O checklist de deploy do Django é claro: DEBUG nunca ligado em produção, e a SECRET_KEY de produção deve ser exclusiva do ambiente e ficar fora do controle de versão (variável de ambiente é uma das formas indicadas)
Um exemplo didático do trecho de settings:
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
DEBUG = False
# Vem do ambiente, nunca do git
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
# Só o domínio, SEM esquema
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(',')
# COM esquema (exigência desde o Django 4.0)
CSRF_TRUSTED_ORIGINS = os.environ.get('DJANGO_CSRF_TRUSTED_ORIGINS', '').split(',')
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
E as variáveis ficam num .env que só existe no servidor, nunca no repositório:
DJANGO_SECRET_KEY=troque-por-uma-chave-unica-de-producao
DJANGO_ALLOWED_HOSTS=exemplo.com.br,www.exemplo.com.br
DJANGO_CSRF_TRUSTED_ORIGINS=https://exemplo.com.br,https://www.exemplo.com.br
E o que é esse STATIC_ROOT? É a pasta pra onde o collectstatic vai copiar todos os estáticos do projeto, e é ela que o Nginx vai servir no caminho do STATIC_URL lá na frente
Resultado esperado: o projeto carrega com os settings de produção, e se a SECRET_KEY não estiver no ambiente o Python já reclama logo de cara, em vez de subir com uma chave errada
Erro comum deste passo: colocar https:// no ALLOWED_HOSTS ou esquecer o esquema no CSRF_TRUSTED_ORIGINS. Um leva só o domínio, o outro leva o domínio COM https://, não inverte!
E tome cuidado com curinga no ALLOWED_HOSTS: ao usar, a validação do cabeçalho Host passa a ser responsabilidade sua
Passo 2: rode o Django com Gunicorn dentro do container
Aqui o Gunicorn substitui o runserver como quem de fato atende as requisições
Que servidor é esse? O Gunicorn é um servidor WSGI para UNIX escrito em Python puro, sem dependências, e se você conhece o runserver, pensa nele como a versão feita pra aguentar produção
Instala via pip e coloca no requirements.txt:
Mais controle no próximo deploy.
Configure sua aplicação em uma VPS Hostinger com acesso root. Escolha os recursos conforme o que seu projeto precisa.
- SSD NVMe
- AMD EPYC
- Backups semanais
python -m pip install gunicorn
O comando base aponta pro módulo WSGI e roda na mesma pasta do manage.py:
gunicorn myproject.wsgi
Só que tem um detalhe: por padrão ele sobe um processo com uma thread, escutando em 127.0.0.1:8000
No seu PC isso funciona, mas dentro do container o 127.0.0.1 é o próprio container, aí o Nginx rodando em outro container não alcança o Gunicorn
Por isso, no exemplo abaixo ele escuta numa interface alcançável pela rede do Compose e já sobe com 3 workers, que é o (2 x 1) + 1 de uma VPS com 1 vCPU como o KVM 1
Um Dockerfile mínimo, como exemplo didático (não testado pelo autor):
# Exemplo didático, não testado pelo autor
# Informe uma versão de Python suportada pelo seu Django e pelo Gunicorn (3.12 ou superior)
ARG PYTHON_VERSION
FROM python:${PYTHON_VERSION}
WORKDIR /app
COPY requirements.txt .
RUN python -m pip install -r requirements.txt
COPY . .
# Roda na pasta do manage.py e escuta fora do 127.0.0.1
CMD ["gunicorn", "myproject.wsgi", "--bind", "0.0.0.0:8000", "--workers", "3"]
Se tu já montou a API NestJS com Docker Compose, a lógica é bem parecida: a aplicação fica num container e o proxy em outro
Erro comum deste passo: manter o bind padrão em 127.0.0.1 e ficar horas tentando entender por que o Nginx não acha o Gunicorn (já vi muita gente perder a tarde com isso haha)
Passo 3: coloque o Nginx na frente e configure o proxy confiável
A documentação de deploy do Gunicorn recomenda fortemente rodar o Gunicorn atrás de um proxy, e o Nginx é a opção mais indicada
O Nginx faz três trabalhos aqui: termina o HTTPS, serve a pasta STATIC_ROOT no caminho STATIC_URL e valida o Host, recusando requisições pra hosts incorretos antes delas chegarem ao Django
Sobre o HTTPS: o certificado do seu domínio precisa estar configurado no Nginx, porque é ele quem termina o TLS e conversa com o Gunicorn por dentro da rede dos containers
Agora o ponto que mais derruba deploy com Docker, se liga nisso:
Por padrão, o Gunicorn só confia nos cabeçalhos X-Forwarded-* quando a conexão vem do localhost (127.0.0.1, o valor padrão de forwarded_allow_ips)
Com o Nginx em OUTRO container, ele não é localhost, então o Gunicorn ignora esses cabeçalhos e o Django nunca fica sabendo que a requisição original veio por HTTPS
A solução é liberar o IP do proxy com --forwarded-allow-ips, informando o IP que o container do Nginx usa na rede do Compose
E o '*'? Só é seguro se o Gunicorn estiver totalmente isolado da rede externa, porque com conexões não confiáveis qualquer um pode forjar esses cabeçalhos
Um docker-compose de exemplo com os dois serviços (exemplo didático, não testado pelo autor):
# Exemplo didático, não testado pelo autor
services:
web:
build:
context: .
args:
PYTHON_VERSION: "SUA_VERSAO_DO_PYTHON"
env_file: .env
command: >
gunicorn myproject.wsgi
--bind 0.0.0.0:8000
--workers 3
--forwarded-allow-ips=IP_DO_NGINX
volumes:
- static:/app/staticfiles
# sem "ports": o Gunicorn não fica exposto para fora
nginx:
image: nginx
ports:
- "80:80"
- "443:443"
volumes:
- static:/srv/static:ro
# aqui entram a sua configuração do Nginx e o certificado do domínio
depends_on:
- web
volumes:
static:
O volume static é o truque pros estáticos: o Django escreve nele via collectstatic e o Nginx lê a mesma pasta e serve no /static/
A mesma ideia de aplicação + proxy aparece no deploy do Astro com SSR, caso tu queira comparar as duas montagens
Do lado do Django, entra o SECURE_PROXY_SSL_HEADER:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Só que essa linha tem regra, e é das sérias: use apenas se as TRÊS condições forem verdadeiras
- o Django está atrás de um proxy
- o proxy remove o
X-Forwarded-Protode todas as requisições que recebe - o proxy só envia esse cabeçalho em requisições que chegaram por HTTPS
Erro comum deste passo: ativar o SECURE_PROXY_SSL_HEADER sem o Nginx limpar o X-Forwarded-Proto recebido. Aí um atacante manda o cabeçalho na mão e faz qualquer requisição parecer segura, ou seja, tu desliga a proteção achando que ligou 😬

Crie sistemas com IA para vender
Prompts, modelos e checklists para criar e publicar seu sistema com IA e preparar a oferta para o primeiro cliente.
Quero o e-book gratuito Receba o livro e os bônus no seu e-mail.Passo 4: suba os containers na VPS e aplique migrations e estáticos
Com o projeto pronto, agora é hora de levar tudo pra VPS e aplicar o que o banco e o Nginx precisam
Na Hostinger com o template Docker, o fluxo fica assim:
- Use o template com Ubuntu 24.04, que já vem com
docker-ceedocker-composeinstalados - Acesse a VPS como root com
ssh root@IP_DA_VPS, ou pelo terminal direto no navegador (a Web Console da Hostinger já abre a sessão logada como root), sem instalar nada no seu PC - Confira a instalação com
docker --versionedocker-compose --version; se os dois responderem com a versão, o ambiente está pronto - No painel da VPS, abra o Docker Manager, oferecido sem custo nas VPS com o template Docker, e implante o seu Compose por URL de repositório (GitHub, GitLab ou link direto) ou colando o YAML manualmente
- Garanta que as variáveis do
.envcheguem ao container sem passar pelo git, porque a SECRET_KEY não pode ir pro repositório de jeito nenhum - Com os containers no ar, rode no container da aplicação os dois comandos abaixo
python manage.py migrate
python manage.py collectstatic
O migrate aplica no banco de produção as migrations que vieram versionadas junto com o código, e o collectstatic copia todos os estáticos pra STATIC_ROOT, que é a pasta que o Nginx serve
O Docker Manager também serve pra acompanhar os containers depois, o que ajuda bastante quando algo cai e tu precisa saber qual deles foi
Erro comum deste passo: criar migrations em produção em vez de trazê-las do desenvolvimento. Migration nasce no ambiente de dev, vai pro git com o código e em produção só roda migrate, beleza?
Passo 5: valide o deploy com check --deploy
A checagem final é o comando que o próprio Django oferece pra caçar problema de segurança e configuração:
python manage.py check --deploy
Rodado contra os settings de produção, ele aponta avisos como security.W004 (SECURE_HSTS_SECONDS não definido), security.W012 (SESSION_COOKIE_SECURE), security.W016 (CSRF_COOKIE_SECURE) e security.W018 (DEBUG ligado)
Resolve o que ele apontar e roda de novo, até não sobrar alerta crítico
Depois confere no navegador:
- o site abre pelo domínio em HTTPS
- CSS, JS e imagens carregam pelo Nginx no
/static/ - um formulário com CSRF (o login do admin já serve) envia sem erro
- os logs do container não mostram o aviso de servidor de desenvolvimento
Erro comum deste passo: rodar o check --deploy com os settings de desenvolvimento e achar que está tudo certo. Tem que rodar DENTRO do container de produção, com o mesmo .env que o site usa
Erros comuns ao publicar Django com Gunicorn e Nginx
Quase todo problema de deploy de Django cai em configuração, não em falta de máquina, então antes de pensar em trocar de plano, passa por esta lista
Bad Request ao abrir o site
- Causa: ALLOWED_HOSTS ausente ou errado com DEBUG = False, e nesse cenário o Django não funciona sem hosts adequados
- Correção: coloque o domínio (e o www, se usar) no ALLOWED_HOSTS, sem
https:// - Prevenção: mantenha os hosts em variável de ambiente e confira o valor antes de cada deploy
Formulário falhando por CSRF
- Causa: CSRF_TRUSTED_ORIGINS sem o esquema, o que não vale desde o Django 4.0
- Correção: use o formato completo, como
https://exemplo.com.br - Prevenção: lembre da regra: ALLOWED_HOSTS sem esquema, CSRF_TRUSTED_ORIGINS com esquema
CSS e JS sem carregar
- Causa: o
collectstaticnão rodou, ou o Nginx não está servindo a pasta STATIC_ROOT no STATIC_URL - Correção: rode
python manage.py collectstatice confirme que o volume dos estáticos está montado no Nginx - Prevenção: inclua o
collectstaticna rotina de todo deploy
Django sem reconhecer o HTTPS atrás do proxy
- Causa: o Gunicorn ignora os
X-Forwarded-*porque o Nginx está em outro container e só conexões do localhost são confiáveis por padrão - Correção: libere o IP do proxy com
--forwarded-allow-ipse só use o SECURE_PROXY_SSL_HEADER se as três condições do Passo 3 forem verdadeiras - Prevenção: nada de
'*'se o Gunicorn tiver qualquer exposição à rede externa
Aviso de servidor de desenvolvimento nos logs
- Causa: o container ainda sobe com
runserver, que não passou por auditorias de segurança nem por testes de desempenho - Correção: troque o comando do container pelo Gunicorn apontando pro
myproject.wsgi - Prevenção: confira o
commanddo Compose e oCMDdo Dockerfile antes de publicar
Vídeo de apoio do canal
Neste vídeo o Matheus apresenta o Hermes Desktop, o app que dá interface ao Hermes Agent e permite rodar o agente de IA local ou numa VPS 😀
Próximo passo: proteja e mantenha o Django em produção
Com o site no ar, a prioridade vira não perder nada quando algo der errado
Na Hostinger, todos os planos de VPS têm backup semanal gratuito, então avalia se o seu banco muda rápido demais pra depender só dele
Antes de atualizar o Django ou as imagens dos containers, tira um snapshot manual
Só toma cuidado: cada VPS guarda um único snapshot por vez, e ele é apagado se você reinstalar o sistema ou restaurar um backup, então não trata o snapshot como backup de longo prazo!
Se o projeto ainda está no Django 6.0, já coloca no calendário a migração pro 6.1, porque o 6.0 só recebe correções de segurança e de perda de dados até abril de 2027
E toda vez que mexer em settings, roda o check --deploy de novo, é rapidinho e evita surpresa em produção
Se o próximo passo é escolher onde rodar, dá pra conferir a VPS para este projeto e começar pelo plano que bate com a quantidade de workers que tu precisa
Até o próximo post! 🙂
Perguntas frequentes
Precisa rodar o Gunicorn dentro de Docker ou dá pra instalar direto na VPS?
O comando gunicorn myproject.wsgi funciona tanto dentro de um container quanto instalado direto na VPS, a lógica de bind e de workers é a mesma nos dois casos. A vantagem de usar Docker é isolar o ambiente da aplicação e padronizar o deploy junto com o Nginx, que é como este post monta o cenário.
O comando check --deploy do Django substitui a revisão do checklist de deploy?
Não totalmente. O check --deploy cobre itens como SECURE_HSTS_SECONDS (W004), SESSION_COOKIE_SECURE (W012), CSRF_COOKIE_SECURE (W016) e DEBUG (W018), mas o checklist oficial de deploy do Django inclui outros pontos que o comando não verifica, como ter STATIC_ROOT configurado e rodar o collectstatic. Use os dois juntos.
Quer aprender mais sobre inteligência artificial e programação na prática? Conheça o canal Hora de Codar no YouTube e acompanhe os próximos vídeos.
Formações
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 48min
Blog | Mais populares

Checklist de segurança n8n VPS pública: guia essencial para proteger sua instalação
Checklist de segurança n8n VPS pública: guia essencial para proteger sua instalação A popularidade da automação de processos com o n8n está em alta, principalmente […]

Checklist de segurança n8n VPS 2026: proteja sua automação
Confira o checklist de segurança n8n VPS 2026 e mantenha sua automação protegida. Veja práticas essenciais e atualizadas para seu servidor. Manter seu ambiente de […]
Quanto custa uma VPS? Preço atualizado 2026
Quando se fala em quanto custa uma VPS, é preciso contextualizar o tema a partir de um provedor específico, porque o preço só faz sentido […]
