Escolhi utilizar uma VPS para o deploy do meu portfólio pelo excelente custo-benefício e controle de gastos. Essa solução permite testar configurações em um ambiente controlado, simulando cenários reais de forma prática e sem surpresas financeiras. Baseei-me no vídeo do canal Dreams of Code, que explica de forma clara como configurar uma VPS.
A seguir, descrevo o passo a passo que segui para configurar minha VPS e que você também poderá seguir para subir o seu projeto
1. Contrate um serviço de VPS para adquirir seu servidor dedicado.
Existem várias opções disponíveis, mas no meu caso, optei pelo serviço da Contabo por apenas 5 dólares mensais, oferecendo 4 núcleos de CPU, 6 GB de RAM, 400 GB de armazenamento SSD, 200 Mbit/s de velocidade de rede e 32 TB de tráfego mensal (com entrada ilimitada),
rodando o Ubuntu Server 22.04.5 LTS
Realize o primeiro acesso ao servidor utilizando o protocolo SSH com o seguinte comando no terminal, utilizando o IP da máquina contratada:
ssh root@<IP-do-servidor>
Encontre o IP da sua máquina para configurar o DNS utilizando o comando
ip addr
Configure o DNS no serviço onde o domínio foi adquirido. Consulte a documentação oficial do provedor para orientações detalhadas sobre como realizar essa configuração.
2. Atualize os pacotes
sudo apt update && sudo apt upgrade -y
3. Criando um Novo Usuário
Trabalhar diretamente com o usuário root pode expor seu servidor a riscos desnecessários, a melhor pratica e criar um novo usurário com privilégios limitados.
adduser newuser
4. Concedendo permissões de sudo
Após a criação do usuário devemos adiciona-lo ao sudo group para que ele possa realizar comandos que exigem privilégios de nível elevado, sem a necessidade de utilizar diretamente o usuário root
usermod -aG sudo newuser
Agora podemos trocar para o novo usuário para darmos inicio a configuração do sistema
su - newuser
5. Removendo autenticação por senha do SSH
Para aumentar a segurança da conexão SSH, é recomendável desativar a autenticação por senha, isso elimina o risco de ataques de brute force baseado em senha.
Antes de desativarmos a autenticação de senha para o SSH, devemos criar um par de chaves SSH para garantir que ainda poderemos acessar o servidor
ssh-keygen -t ed25519 -C "useremail@email.com"
## -t ed25519: Especifica o tipo da chave a ser gerada.
## -C "seuemail@exemplo.com: Adiciona um comentário à chave Durante a execução do comando, você deverá escolher o local onde deseja salvar a chave ou optar pelo local padrão: `~/.ssh/chave`
Após a criação da chave devemos adicionar a chave SSH ao servidor
ssh-copy-id -i ~/.ssh/sshkey.pub newuser@serverip
- indica o caminho onde a sua chave esta localizado localmente -i ~/.ssh/sshkey.pub
- Esse comando copia a chave pública SSH para o arquivo
~/.ssh/authorized_keys
no servidor remoto, permitindo o acesso para o usuário com a chave criada
Agora tente acessar o servidor utilizado a chave SSH privada localizada no mesmo diretório da chave publica
ssh -p 22 -i ~/.ssh/sshkey newuser@serverip
- -p especifica a porta SSH definida no servidor
- -i ~/.ssh/sshkey especifica o caminho da chave privada
Se acesso utilizando a chave SSH der certo podemos remover a autenticação por senha do SSH.
Primeiro abra o arquivo de configuração SSH
sudo nano /etc/ssh/sshd_config
Localize e altere as seguintes linhas no arquivo
## HARDENING SSH CONECTION
PermitRootLogin no ## BLOQUEIA O LOGIN DIRETO COM ROOT
PasswordAuthentication no ## DESATIVA CONEXÃO POR SENHA
UsePAM no ## DESATIVA O USO DE PAM NO SSH
após alterar essas linhas salve e feche o arquivo de configuração e reinicie o serviço de SSH:
sudo systemctl reload ssh
para validar se as alterações estão funcionando tente se conectar
root@serverip # a resposta deve ser essa root@serverip Permission denied (publickey).
Conecte-se ao novo usuário utilizando o SSH.
6. Instale o docker e docker-compose
Para configurar o ambiente, prossiga com a instalação do Docker e do Docker Compose seguindo a documentação oficial:
https://docs.docker.com/engine/install/ubuntu/
https://docs.docker.com/compose/install/linux/
Após instalar o Docker, execute esta imagem para verificar se ele está funcionando corretamente.
docker run -p 80:80 -d nginxdemos/hello
sudo systemctl enable docker
Adiciona o serviço docker à lista de serviços que são iniciados automaticamente na inicialização do sistema.
sudo usermod -aG docker newuser
Adiciona o usuário newuser ao grupo docker, concedendo permissões para que ele possa executar comandos com o Docker sem a necessidade de utilizar sudo
7. Configurar Firewall
sudo ufw default deny incoming
Bloqueia todas as conexões de entrada por padrão para maior segurança
sudo ufw default allow outgoing
Permite todas as conexões de saída por padrão para permitir o tráfego de saída.
sudo ufw allow OpenSSH
Permite conexões SSH (porta 22) para acesso remoto seguro ao servidor.
sudo ufw allow 80
Permite conexões HTTP (porta 80) para acesso web não criptografado.
sudo ufw allow 443
Permite conexões HTTPS (porta 443) para acesso web seguro e criptografado.
Para que o Docker respeite as regras do UFW, edite o arquivo de configuração do Docker
sudo nano /etc/docker/daemon.json
{
"iptables": false
}
Reinicie o docker
sudo systemctl restart docker
8. Configuração do Docker file para o Projeto
Crie o arquivo Dockerfile na raiz do seu projeto para a criação da imagem docker
# Stage 1: Construção da aplicação usando Node.js
FROM node:22 AS build
# Define o diretório de trabalho dentro do container para organizar os arquivos
WORKDIR /app
# Copia os arquivos de configuração do Node.js (package.json e package-lock.json) para o container
COPY package*.json ./
# Instala as dependências do projeto
RUN npm install
# Copia o restante dos arquivos do projeto para o container
COPY . .
# Executa o comando para construir a aplicação (adapte este comando conforme o projeto)
RUN npm run build
# Stage 2: Configuração do servidor Nginx para servir a aplicação
FROM nginx:alpine
# Define o diretório de trabalho do Nginx onde os arquivos estáticos serão armazenados
WORKDIR /usr/share/nginx/html
# Copia os arquivos da build gerada no estágio anterior para o diretório do Nginx
COPY --from=build /app/dist/codebyfernandes/browser .
# Copia o arquivo de configuração personalizado do Nginx (opcional, se necessário)
COPY default.conf /etc/nginx/conf.d/default.conf
# Expõe a porta 80 para o servidor
EXPOSE 80
# Define o comando padrão para iniciar o Nginx
CMD ["nginx", "-g", "daemon off;"]
Crie o arquivo default.conf na raiz do projeto:
server {
listen 80; # Porta onde o servidor vai escutar
server_name localhost; # Nome do servidor (pode ser substituído pelo domínio)
root /usr/share/nginx/html; # Diretório onde estão os arquivos da aplicação
index index.html; # Arquivo principal da aplicação
# Rota principal para servir o Angular
location / {
try_files $uri $uri/ /index.html; # Redireciona todas as rotas para o index.html
}
# Configuração de cache para arquivos estáticos
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|webmanifest)$ {
expires 6M; # Define o tempo de expiração para 6 meses
access_log off; # Desativa logs para esses arquivos
add_header Cache-Control "public"; # Permite cache público
}
error_page 404 /index.html; # Redireciona erros 404 para o index.html
# Logs personalizados
error_log /var/log/nginx/angular-error.log; # Registro de erros
access_log /var/log/nginx/angular-access.log; # Registro de acessos
}
Agora crie a imagem localmente e rode ela em seguida
docker build -t meu-app:1.0 .
docker run -p 80:80 -d meu-app:1.0
-
d
: Executa o container em segundo plano. -
p 80:80
: Mapeia a porta 80 do container para a porta 80 do host.
Se tudo estiver configurado corretamente, você poderá acessar seu projeto em: http://localhost
Agora envie sua imagem para o Docker Hub:
Docker Hub: Build and Push First Image
9. Configuração do docker compose
Crie o arquivo compose.yaml na raiz do seu projeto.
Primeiro vamos adicionar a imagem do seu web app
services:
# Define os serviços do projeto
webapp:
# Nome do serviço
image: fernandeeess/portfolio-app:prod
# Imagem do contêiner usada para o serviço (versão de produção)
Agora vamos configurar o reverse proxy para o seu projeto utilizando o Traefik, que simplifica a gestão ao configurar automaticamente o certificado SSL e ajustar o load balancer de acordo com os serviços criados.
services:
reverse-proxy:
# Define o serviço do reverse proxy
image: traefik:v3.1
# Especifica a imagem do Traefik que será usada
command:
# Ativa o provedor Docker
- "--providers.docker"
# Desativa a exposição automática de serviços no Traefik
- "--providers.docker.exposedbydefault=false"
# Configura a porta para HTTPS (websecure)
- "--entryPoints.websecure.address=:443"
# Configura o desafio TLS para emissão do certificado SSL
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
# Define o e-mail para registrar os certificados SSL
- "--certificatesresolvers.myresolver.acme.email=youremail"
# Define o local para armazenar os certificados SSL
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
# Configura a porta para HTTP (web)
- "--entrypoints.web.address=:80"
# Redireciona HTTP para HTTPS
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
ports:
# Mapeia as portas do host para o contêiner (HTTP e HTTPS)
- "80:80"
- "443:443"
volumes:
# Volume para armazenar certificados SSL
- letsencrypt:/letsencrypt
# Permite que o Traefik se comunique com o Docker para detectar serviços
- /var/run/docker.sock:/var/run/docker.sock
webapp:
# Define o serviço da aplicação web
image: fernandeeess/portfolio-app:prod
# Especifica a imagem do contêiner da aplicação
volumes:
letsencrypt:
# Volume para armazenar os dados do Let's Encrypt
Adicione as labels ao web app service
webapp:
# Define o serviço da aplicação web
image: fernandeeess/portfolio-app:prod
# Especifica a imagem do contêiner que será usada para o serviço
labels:
# Habilita o Traefik para este serviço
- "traefik.enable=true"
# Define a regra de roteamento baseada no domínio
- "traefik.http.routers.webapp.rule=Host(`yourdomain.com`)"
# Define o entrypoint como HTTPS (websecure)
- "traefik.http.routers.webapp.entrypoints=websecure"
# Configura o resolver de certificado SSL a ser usado
- "traefik.http.routers.webapp.tls.certresolver=myresolver"
# Define a porta interna do contêiner que será usada pelo load balancer
- "traefik.http.services.webapp.loadbalancer.server.port=80"
deploy:
# Configuração de implantação do serviço
mode: replicated
# Define o número de réplicas (instâncias) do serviço
replicas: 3
Configure o serviço do Watchtower para monitorar a imagem Docker com a tag prod
. Sempre que houver uma atualização, o Watchtower irá puxar as alterações e reiniciar os serviços automaticamente.
# Continuous Delivery/Deployment
watchtower:
image: containrrr/watchtower # Imagem do Watchtower.
command:
- "--label-enable" # Monitora apenas serviços com labels específicas.
- "--interval" # Define o intervalo de verificação.
- "30" # Verifica atualizações a cada 30 segundos.
- "--rolling-restart" # Reinicia os serviços de forma gradual.
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Permite ao Watchtower gerenciar containers.
# Adicione esta label ao serviço "webapp" para que o Watchtower o monitore:
# - "com.centurylinklabs.watchtower.enable=true"
O arquivo final deve ficar assim:
services:
watchtower:
image: containrrr/watchtower
command:
- "--label-enable"
- "--interval"
- "30"
- "--rolling-restart"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
reverse-proxy:
image: traefik:v3.1
command:
- "--providers.docker"
- "--providers.docker.exposedbydefault=false"
- "--entryPoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=email@email.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
ports:
- "80:80"
- "443:443"
volumes:
- letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
webapp:
image: fernandeeess/portfolio-app:prod
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.webapp.entrypoints=websecure"
- "traefik.http.routers.webapp.tls.certresolver=myresolver"
- "traefik.http.services.webapp.loadbalancer.server.port=80"
- "com.centurylinklabs.watchtower.enable=true"
deploy:
mode: replicated
replicas: 3
volumes:
letsencrypt:
Se tudo estiver configurado corretamente, você poderá acessar seu projeto no seu domínio configurado
10. Configure o CI utilizando o GitHub Actions
Automatize o processo de construção da imagem Docker e envio ao Docker Hub. Siga o tutorial disponível no link abaixo:
Automate Docker Image Builds and Push to Docker Hub Using GitHub Actions
Lembre-se de que a imagem deve estar com a tag "prod" para que o Watchtower possa puxar as alterações e reiniciar os serviços.
11. Links
- Portfolio
- Instalação e Configuração do Docker:
- Documentação e Conceitos do Docker:
- Recursos Adicionais e Ferramentas Relacionadas:
- Firewall e Práticas de Segurança:
Top comments (0)