v2.0

Postado em: 30-08-2025 às 12:31:37 PM · 9 min de leitura

Deploy de Aplicações com Portainer e NGINX


Configurando um portainer + Docker + Nginx em uma VPS para acessar suas aplicações em produção com um gerenciador de containers poderoso. Para aqueles que querem ter um controle mais sofisticado em relação a alternativas como Caprover.

Caso não tenha o Docker instalado na sua VPS


# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
   https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Instalar o docker

 sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin \ 
 docker-compose-plugin 

Instalando o portainer e NGINX Proxy Manager (NPM)

Crie um volume no docker para guardar os dados do portainer

 docker volume create portainer_data 

Crie um container do portainer e associe o volume a ele

 docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always \ 
 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:lts 

Agora é possivel acessar o seu portainer no http://[SEU_IP]:9443 , perceba o https. Na primeira vez que acessar será solicitado a criação do seu usuário admin. 

Agora vamos instalar o nosso gerenciador de proxy nginx para poder acessar o nosso portainer via dominio, mas antes vamos tentar acessar o nosso portainer pelo dominio.

Primeiro, dentro da sua VPS crie uma network para o nosso proxy e já conecta ela ao nosso portainer com os seguintes comandos:

 docker network create nginx-proxy

 docker network connect nginx-proxy portainer

Em seguida vamos acessar o painel do nosso portainer online e vamos criar uma "STACK" com a imagem do nginx proxy manager (NPM), no painel lateral esquerdo acesse stack e crie uma nova stack e em Web Editor adicione o seguinte código do docker-compose.yaml

version: '3'

services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: always
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - nginx-proxy-manager-data:/data
      - letsencrypt-certificates:/etc/letsencrypt
    networks:
      - nginx-proxy

networks:
  nginx-proxy:
    external: true # <- como criamos a network externamente via comando

volumes:
  nginx-proxy-manager-data:
  letsencrypt-certificates:

Salve ela com algum nome fácil, para facilitar usei  nginx-proxy-manager  e quando ele estiver rodando acesse a sua VPS e tente conectar a sua network ao container do proxy, você perceberá que ele vai avisar que já está conectado.

 docker network connect nginx-proxy nginx-proxy-manager 

Agora com o proxy nginx rodando, você consegue acessar o painel com o http:[SEU_IP]:81 , perceba o http. Na primeira vez vai lhe ser solicitado os dados de login, por padrão o login é “admin” ou "[email protected]" e a senha é “changeme”. Assim que você se autenticar ele vai solicitar para que altere os dados, faça-o.

Agora antes de adicionar um dominio ao painel, vamos ter que apontar um subdominio para o IP da nossa VPS usando A Record, vamos usar por padrão portainer.[seudominio.com] por exemplo:

Type: A
Content: SEU_IP_DA_VPS
Name: portainer

Proxy: Desativado ← caso esteja na cloudflare

Agora acesse o painel do nginx http://SEU_IP:81 e configure o dominio no nginx-proxy-manager, nele podemos criar tanto o proxy quanto os certificados SSL.

Primeiro crie um novo host proxy e na aba DETALHES preencha assim

  • Nomes de Domínio: portainer.[seudominio.com] - Este é o nome DNS do Registro A que você criou para o NGINX-Proxy-Manager 
  • Esquema ou Schema: Deixe como HTTP
  • Host/IP de Encaminhamento ou Foward Host/IP: Esse é o nome do container, usaremos portainer, Como o Portainer e o nosso NGINX-Proxy-Manager estão na mesma rede Docker que configuramos lá atrás, poderemos usar o nosso proxy manager para vincular o dominio ao host do container. Lembre-se, o container alvo tem que tá na mesma network nginx-proxy que o proxy, assim o nosso proxy pode encontrar o container alvo.
  • Porta: Usaremos 9443, a porta do container (Não é a porta exposta da aplicação e sim a porta do proprio container).
  • Websockets support: Ligado

Agora na ABA SSL configure o SSL com o seguinte:

  • Certificado SSL: Selecione a opção Solicitar um novo Certificado SSL 
  • Forçar SSL: Habilite nesse caso
  • Suporte HTTP/2: Habilite nesse caso 
  • As outras opções vão depender do seu app
  • No email: Insira seu endereço de e-mail para que o Let's Encrypt o avise quando seu certificado SSL estiver prestes a expirar. O NPM vai renovar seus certificados automaticamente, mas ainda é bom saber se seu certificado está prestes a expirar caso a renovação falhe. 
  • Você pode também fazer a mesma configuração para acessar o painel do nginx como estamos fazendo no portainer.

No final semelhante a isso:

Agora você poderá acessar o seu portainer pelo portainer.[seudominio.com] e não precisa usar a porta 9443, porque o NPM já resolverá isso para nós.

Instalando nosso próprio Docker Hub.

Vamos usar o docker registry para subir os deploys da nossa aplicação, já que a conta gratis do Docker Hub só permite 1 app privado, então não é interessante para quem quer produção.

Rode os seguintes comandos para garantir que o nosso registry seja criado e que esteja com autenticação simples, na sua VPS use os seguintes comandos.

 sudo apt update 

 sudo apt install apache2-utils 

Agora vamos gerar a autenticação no nosso registry com os comandos

 mkdir -p ~/registry-auth 
 

 htpasswd -Bc ~/registry-auth/htpasswd admin 

O "admin" é o nosso arquivo com o nome do usuário e as senhas que usaremos para acessar o container do registry. 

Na sequencia vamos rodar o container do registry e já atribuir a ele a autenticação

 docker run -d -p 5000:5000 --name registry \ 
 --restart=always   -v registry_data:/var/lib/registry \
   -v ~/registry-auth:/auth \
     -e "REGISTRY_AUTH=htpasswd" \
     -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
     -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"   registry:2

Teste acessando dentro de sua VPS use 

curl -u [seu_usuario_htpasswd]:[senha_do_seu_usuario_htpasswd] http://localhost:5000/v2/_catalog

você terá um resultado parecido com:

{"repositories":[]}

Agora você também pode configurar o registry no seu NPM, vá nas configurações de seu DNS e adicione o dominio que vai usar, vamos chamar de registry.[seudominio.com] nesse exemplo:

Primeiro crie um novo host proxy, lembre-se que o registry tem que estar na mesma network do proxy, você pode fazer isso direto no VPS com o comando 

docker network connect nginx-proxy registry 

ou no nosso portainer ao acessar o nosso container e configurar lá a network.

Agora no nosso NPM e na aba DETALHES preencha assim

  • Nomes de Domínio: registry.[seudominio.com]
  • Esquema: Deixe como HTTP
  • Foward Host/IP: registry que é o nome do nosso container
  • Porta: 5000.
  • Websockets support: Desligado para esse exemplo

Agora na ABA SSL configure o SSL com o seguinte:

  • Certificado SSL: Selecione a opção Solicitar um novo Certificado SSL 
  • Forçar SSL: Habilite nesse caso
  • Suporte HTTP/2: Habilite nesse caso 
  • Insira seu email etc.

Pronto, dando tudo certo você agora pode logar na sua máquina local no nosso registry com

 docker login registry.[seudominio.com] 

Finalmente agora temos tudo pronto para prosseguir e subir nossa primeira aplicação no portainer. Para isso vamos subir um backend básico junto com um banco de dados postgres.

Deploy da primeira aplicação

Vamos primeiro criar a network database_net para conectar o nosso backend ao banco, você pode criar ela com o comando direto na vps ou no portainer nas opções de network e criar uma nova lá.

Primeiro vamos subir nosso banco de dados, para isso você pode usar algum template pronto do portainer, mas vamos usar uma config aqui criando uma STACK. No painel do portainer na barra lateral esquerda selecione STACK e na opção de nova stack e adicione o seguinte:

version: '3.8'

services:
  postgres:
    image: postgres:16.10
    container_name: postgres     # <- Para que o nome do container seja fixo
    restart: always
    environment:
      POSTGRES_USER: seu_usuario_postgres # <- Editar aqui
      POSTGRES_DB: nome_do_banco          # <- Editar aqui
      POSTGRES_PASSWORD: senha_do_banco   # <- Editar aqui
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - database_net
    command: ["postgres", "-c", "max_connections=100"]

volumes:
  postgres_data:   
    name: postgres_data # <- Vamos dar o nome para que o volume tenha nome fixo
    
networks:
  database_net:   
    external: true # <- apenas se ela já existir

Confirme e crie a sua stack do postgres

 

Subindo a primeira aplicação

O Nosso backend é em Nodejs, vamos subir um app com o seguinte arquivo Dockerfile.prod

FROM --platform=linux/arm64 node:20

# Caso a plataforma for ARM use o FROM acima


RUN mkdir /home/node/app 

WORKDIR /home/node/app

COPY package*.json ./

RUN npm install

COPY . /home/node/app/

EXPOSE 80

CMD [ "npm", "run", "prod:migrations" ]

Estamos usando a porta 80 dentro do container mas no nosso container ao montar via compose precisaremos usar uma porta diferente para que não dê conflito, já que cada app precisa de uma porta diferente.

Com esse arquivo em mente dentro do diretorio raiz da nossa aplicação, vamos usar os comandos build do docker e push para enviar ao nosso registry.

Build

 docker build -f ./docker/Dockerfile.prod -t registry.[seudominio.com]/seu_app_backend:latest . 

Deploy

 docker push registry.[seudominio.com]/seu_app_backend:latest 

Tendo isso finalizado, vamos criar uma nova stack, dessa vez vamos acessar o nosso portainer e usar o seguinte arquivo yaml, vamos passar também 

version: "3.8"

services:
  seu_app_backend:
    image: registry.[seudominio.com]/seu_app_backend:latest
    container_name: seu_app_backend
    restart: always
    ports:
      - 8888:80     # <- 8888 para export e que possa ser criado caso o container esteja na 80
    environment:
      - DEFAULT_ADMIN_USERNAME=${DEFAULT_ADMIN_USERNAME}
      - DEFAULT_ADMIN_PASSWORD=${DEFAULT_ADMIN_PASSWORD}
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_PORT=${DB_PORT}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_DIALECT=${DB_DIALECT}
      - APP_HOST=${APP_HOST}
      - APP_PORT=${APP_PORT}
      - APP_PROD=${APP_PROD}
      - APP_DEBUG=${APP_DEBUG}
      - APP_SECRET_KEY=${APP_SECRET_KEY}
      - JWT_SECRET_KEY=${JWT_SECRET_KEY}
    networks:
      - database_net   # <- Para que o backend se comunique com o banco de dados
      - nginx-proxy    # <- para que possamos expor o nosso backend online
    volumes:
       - seu_app_backend_data:/home/node/app/src/public/uploads

networks:
  database_net:
    external: true
  nginx-proxy:
    external: true
    
volumes:
  seu_app_backend_data:
    name: seu_app_backend_data

Como pode ver esse arquivo permite passar variaveis de ambiente via portainer, já que nele também podemos usar elas, assim o nosso container sempre vai carregar elas.

Antes de confirmar passe as suas variaveis na configuração e finalize a criação do seu container, assim quando ele estiver online basta adicionar no Gerenciador DNS e seu NPM o seu novo host e pronto. 

Agora no nosso NPM e na aba DETALHES preencha assim

  • Nomes de Domínio: api.[seudominio.com]
  • Esquema: Deixe como HTTP
  • Foward Host/IP: seu_app_backend que é o nome do nosso container
  • Porta: 8888.
  • Websockets support: Desligado para esse exemplo

Agora na ABA SSL configure o SSL com o seguinte:

  • Certificado SSL: Selecione a opção Solicitar um novo Certificado SSL 
  • Forçar SSL: Habilite nesse caso
  • Suporte HTTP/2: Habilite nesse caso 
  • Insira seu email etc.

Agora seu app backend estará disponivel para ser acessado via https://api.[seudominio.com]

Fontes interessantes:

https://docs.portainer.io/start/install-ce/server/docker/linux 

https://www.linode.com/community/questions/24444/how-do-i-install-nginx-proxy-manager-on-portainer

https://digitalinterativo.com.br/como-instalar-o-postgres-no-portainer/

 
https://medium.com/@psnavya90/deploying-a-simple-web-application-using-portainer-f280e61185d3


https://joshbuker.com/blog/using-portainer-and-github-for-continuous-deployment/
 

https://github.com/orgs/portainer/discussions/10597 

Tags:
Posts relacionados
Redes Neurais e algoritmos genéticos - Joguinhos simples
ESP8266 Projeto - Consultando cotações em APIs de criptomoedas
NodeMCU ESP8266 - As placas para IoT
MH-ETLive ATTINY88 16mhz Nano V3: Parece um Arduino Nano, mas não é Arduino!