Servidor VPS (Cloud)
Como hospedar vários sites na mesma VPS com Nginx
Use este guia com IA
Escolha o que você quer e abra no seu assistente preferido.
1 O que a IA deve fazer?
2 Abrir no assistente
Neste artigo
Resposta rápida: para rodar vários sites na mesma VPS, crie um server block do Nginx por domínio em sites-available, ative com um link em sites-enabled e adicione um bloco default_server que recusa domínios desconhecidos. Dê a cada site PHP um usuário Linux e um pool do PHP-FPM próprios, emita um certificado por domínio com o Certbot e aponte o DNS de todos para o mesmo IP. Uma VPS de 4 GB atende um conjunto pequeno de sites estáticos ou WordPress leves.
Quando faz sentido juntar sites em uma VPS
Agências, freelancers e desenvolvedores costumam reunir projetos pequenos em uma VPS para reduzir custo. Um servidor com dez sites de pouco tráfego custa bem menos que dez hospedagens separadas, e você administra um sistema só.
O preço dessa economia é que os sites dividem CPU, memória e disco. Um site com pico ou sob ataque deixa os vizinhos lentos, e uma configuração quebrada no Nginx pode derrubar todos. Quando um site vira o principal do negócio, mova-o para uma VPS própria antes de um incidente.
Como o Nginx escolhe qual site abrir
Todos os domínios chegam ao mesmo IP. O Nginx separa as requisições pelo nome enviado no cabeçalho Host e, no HTTPS, pelo nome enviado no início da conexão TLS (SNI). Ele compara esse nome com o server_name de cada bloco, nesta prioridade:
| Prioridade | Tipo de server_name | Exemplo |
|---|---|---|
| 1 | Nome exato | loja.com.br |
| 2 | Curinga no início (o mais longo vence) | *.loja.com.br |
| 3 | Curinga no fim (o mais longo vence) | loja.* |
| 4 | Expressão regular (vale a primeira na ordem) | ~^(www\.)?loja\.com\.br$ |
| 5 | Nenhum casou: servidor padrão da porta | O bloco com default_server, ou o primeiro carregado |
A última linha causa surpresas. Sem um default_server explícito, qualquer domínio apontado para o seu IP, ou o próprio IP digitado no navegador, abre o primeiro site carregado. Isso expõe o site de um cliente em um endereço que não é dele e confunde o Google. A correção está na seção de SSL e segurança.
Estrutura: sites-available e sites-enabled
No Ubuntu e no Debian, cada site fica em um arquivo próprio em /etc/nginx/sites-available/, e o Nginx só carrega o que estiver linkado em /etc/nginx/sites-enabled/. Nomeie cada arquivo com o domínio:
# ativar um site
sudo ln -s /etc/nginx/sites-available/site1.com.br /etc/nginx/sites-enabled/
# desativar o site de exemplo que vem com o pacote
sudo rm /etc/nginx/sites-enabled/default
# testar antes de recarregar, sempre
sudo nginx -t && sudo systemctl reload nginxRode nginx -t sempre antes de recarregar. Um erro de sintaxe em um site impede o Nginx de aceitar a configuração nova de todos, e um reinício da VPS com esse erro deixa todos fora do ar. Se ainda não tem o Nginx, veja como instalar o Nginx.
Server blocks para site PHP e site estático
Site WordPress ou outra aplicação PHP, em /etc/nginx/sites-available/site1.com.br, já usando o pool próprio da próxima seção:
server {
listen 80;
server_name site1.com.br www.site1.com.br;
root /var/www/site1.com.br/public;
index index.php index.html;
access_log /var/log/nginx/site1.com.br.access.log;
error_log /var/log/nginx/site1.com.br.error.log;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock;
}
location ~ /\.(?!well-known) {
deny all;
}
}Site estático, em /etc/nginx/sites-available/site2.com.br:
server {
listen 80;
server_name site2.com.br www.site2.com.br;
root /var/www/site2.com.br/public;
index index.html;
access_log /var/log/nginx/site2.com.br.access.log;
error_log /var/log/nginx/site2.com.br.error.log;
location / {
try_files $uri $uri/ =404;
}
}Logs separados mostram de onde vem cada erro e cada pico, e a rotação padrão do Ubuntu já cuida deles. Para uma aplicação Node, Python ou container, troque o bloco do PHP por um proxy_pass, como no guia de Next.js na VPS. O exemplo usa PHP 8.3, padrão do Ubuntu 24.04; ajuste o número se a sua versão for outra.
Isolamento: um usuário e um pool do PHP por site
Na instalação padrão, todo o PHP roda como o mesmo usuário em um único pool. Um plugin vulnerável em um site consegue ler e alterar os arquivos de todos. O isolamento básico é dar a cada site um usuário Linux sem login e um pool do PHP-FPM rodando com esse usuário.
Usuário e permissões
sudo useradd --system --user-group --home-dir /var/www/site1.com.br --shell /usr/sbin/nologin site1
sudo mkdir -p /var/www/site1.com.br/public
sudo chown -R site1:www-data /var/www/site1.com.br
sudo find /var/www/site1.com.br -type d -exec chmod 2750 {} +
sudo find /var/www/site1.com.br -type f -exec chmod 640 {} +- Dono site1: o PHP do site lê e grava os próprios arquivos, como uploads e cache.
- Grupo www-data com leitura: o Nginx lê CSS, imagens e JavaScript, mas não grava nada.
- Nenhuma permissão para outros: o usuário do site2 não consegue nem listar a pasta do site1.
- O 2 do 2750: é o bit setgid, que faz arquivos novos herdarem o grupo da pasta. Sem ele, um upload feito pelo PHP não seria servido pelo Nginx.
Um pool do PHP-FPM por site
Crie /etc/php/8.3/fpm/pool.d/site1.conf:
[site1]
user = site1
group = site1
listen = /run/php/php8.3-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 30s
pm.max_requests = 500sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
ls -l /run/php/O modo ondemand é o que torna vários sites viáveis com pouca RAM: sem visitas, o pool não mantém processos; eles nascem na primeira requisição e morrem depois de 30 segundos parados. O pm.max_children limita quanto um site pode crescer, e o pm.max_requests recicla processos para conter vazamentos de memória de plugins. Para clientes que não se conhecem, containers ou VPS separadas isolam ainda mais.
DNS, SSL e segurança
Cada domínio precisa de registros A, no domínio raiz e no www, apontando para o IP da VPS. Com o DNS resolvendo, emita um certificado por site:
sudo certbot --nginx -d site1.com.br -d www.site1.com.br
sudo certbot --nginx -d site2.com.br -d www.site2.com.br
sudo certbot certificates
sudo certbot renew --dry-runNão junte domínios de clientes diferentes em um certificado só: se um cliente sai e o domínio deixa de apontar para a VPS, a renovação falha para todos os sites daquele certificado.
Agora o bloco padrão que barra domínios desconhecidos, em /etc/nginx/sites-available/padrao:
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}O return 444 fecha a conexão sem resposta, e o ssl_reject_handshake (Nginx 1.19.4 ou mais novo) recusa o HTTPS de nomes sem bloco próprio, sem precisar de certificado. Ative com um link em sites-enabled e teste com curl -I http://IP_DA_VPS, que deve falhar sem resposta.
- Firewall: libere só 80, 443 e o SSH, de preferência em porta alterada.
- Fail2ban: opcional, contra força bruta no SSH e no login do WordPress.
- Backup: copie arquivos e bancos de cada site para fora da VPS, como em backup automático com rsync.
Erros comuns com vários sites
| Sintoma | Causa provável | Solução |
|---|---|---|
| 502 Bad Gateway em um site PHP | Socket do server block diferente do pool, ou pool parado | Compare fastcgi_pass com o listen do pool e veja o status do PHP-FPM |
| 403 Forbidden em tudo | Nginx não atravessa a pasta ou falta o arquivo de índice | Confira dono, grupo e o modo 2750 das pastas |
| Um domínio abre o site de outro | Nome faltando no server_name (como o www) ou DNS errado | Revise o server_name e teste com dig |
| Upload funciona, mas a imagem não aparece | Arquivo criado sem o grupo www-data | Reaplique o grupo e o bit setgid |
| Certbot falha em um domínio | Domínio ainda não aponta para a VPS | Confirme o registro A e rode o Certbot só para esse site |
Para ver erros de um site em tempo real, use sudo tail -f /var/log/nginx/site1.com.br.error.log. Para testar um site antes de mudar o DNS, force o nome: curl -I -H "Host: site1.com.br" http://IP_DA_VPS.
Limites e qual VPS usar na RoxyCloud
Com um usuário por site, fica fácil ver quanto cada um consome:
ps -o user=,rss= -C php-fpm8.3 | awk '{m[$1]+=$2} END {for (u in m) printf "%s %.0f MB\n", u, m[u]/1024}'
free -hQuando a soma dos sites passar de 70% da RAM de forma constante, suba de plano ou separe as cargas.
| Conjunto de sites | Plano de partida |
|---|---|
| Até 5 WordPress leves ou dezenas de estáticos | VPS Xeon 4 GB (3 vCPU, 40 GB NVMe) |
| Agência com 10 a 15 sites e uma loja pequena | VPS Xeon 8 GB (4 vCPU, 55 GB NVMe) |
| Sites com muito PHP fora do cache | VPS Ryzen 9 8 GB (3 vCPU, 100 GB NVMe) |
Na RoxyCloud, a VPS Xeon é a escolha padrão para vários sites, por entregar mais núcleos pelo mesmo preço. Todas as VPS têm acesso root, IPv4 dedicado, Anti-DDoS incluso e ficam no datacenter próprio em Presidente Prudente (SP). Se você não quer administrar servidor, a hospedagem de sites com cPanel também comporta vários domínios.
Perguntas frequentes
Quantos sites cabem em uma VPS de 4 GB?
Com pools do PHP em modo ondemand e cache de página, de 3 a 5 sites WordPress leves ou dezenas de sites estáticos. Loja WooCommerce ou site com muito tráfego pede mais RAM ou uma VPS própria.
Um site invadido afeta os outros?
Sem isolamento, sim. Com um usuário e um pool do PHP por site e pastas fechadas para os demais, o estrago fica contido. Para clientes que não se conhecem, containers ou VPS separadas isolam ainda mais.
Preciso de um IP por site?
Não. O Nginx escolhe o site pelo cabeçalho Host e, no HTTPS, pelo SNI. Um único IP atende todos os domínios, cada um com o seu certificado.
Por que um domínio desconhecido abre um dos meus sites?
Porque, sem um bloco default_server, o Nginx usa o primeiro bloco carregado. Crie um bloco padrão que responde 444 e rejeita o handshake TLS.
Um certificado para todos os sites ou um por site?
Um por site. Um certificado com todos os domínios falha na renovação se um deles deixar de apontar para a VPS. Separados, cada um renova sozinho.
Precisa de mais ajuda?
Abra um ticket, entre no Discord ou acesse o painel da RoxyCloud.