Introdução
Deploy de Rust em produção costuma cair em dois extremos: ou um Kubernetes completo para uma aplicação pequena, ou um binário copiado por SSH sem logs, health check nem rollback. Para a maioria dos produtos pequenos e médios, uma VPS com Docker, systemd e Nginx é o ponto de equilíbrio: simples de operar, barato, reproduzível e seguro o bastante para tráfego real.
Este guia mostra um caminho pragmático para publicar uma API Rust, especialmente uma aplicação Axum ou Actix Web, em uma VPS Linux. A ideia é sair de cargo run local para um serviço com:
- imagem Docker pequena;
- variáveis de ambiente separadas do código;
- Nginx com TLS na frente;
- health check para monitoramento;
- logs via
journalctle Docker; - rollback rápido quando algo der errado.
Se você ainda não otimizou a imagem, leia também o guia de Rust e Docker para builds de produção, o artigo de CI/CD para Rust e a página de Axum no ecossistema Rust quando a aplicação for uma API HTTP. Aqui vamos focar no servidor.
Arquitetura Recomendada
A topologia é simples:
Internet
|
v
Nginx :443 ---> container rust-api :3000
|
+-- /healthz para monitoramento externo
Nginx termina TLS, aplica limites básicos e encaminha as requisições para a aplicação Rust rodando em 127.0.0.1:3000 ou em uma rede Docker local. A aplicação não precisa expor porta pública diretamente.
Preparando a API Rust
Tenha pelo menos um endpoint de saúde. Em Axum:
use axum::{routing::get, Router};
use std::net::SocketAddr;
async fn healthz() -> &'static str {
"ok"
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.json()
.with_env_filter(std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()))
.init();
let app = Router::new()
.route("/healthz", get(healthz));
let addr: SocketAddr = "0.0.0.0:3000".parse().unwrap();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
tracing::info!(%addr, "servidor iniciado");
axum::serve(listener, app).await.unwrap();
}
O endpoint deve ser barato, sem depender de APIs externas. Se a aplicação usa Postgres ou Redis, crie um segundo endpoint mais profundo, como /readyz, para checar dependências internas quando fizer sentido.
Dockerfile Multi-Stage
Um Dockerfile mínimo e eficiente:
FROM rust:1.88-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends pkg-config libssl-dev ca-certificates && rm -rf /var/lib/apt/lists/*
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
FROM debian:bookworm-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
RUN useradd -r -u 10001 appuser
COPY --from=builder /app/target/release/minha-api /usr/local/bin/minha-api
USER appuser
EXPOSE 3000
ENV RUST_LOG=info
CMD ["/usr/local/bin/minha-api"]
Para projetos maiores, substitua a etapa de build por cargo-chef, como no guia de builds Docker otimizados. O importante é não rodar a aplicação final dentro da imagem rust:*.
docker-compose.yml de Produção
Mesmo com um único serviço, docker compose ajuda a padronizar restart, env file e rede:
services:
rust-api:
image: registry.example.com/minha-api:2026-05-19
container_name: rust-api
restart: unless-stopped
env_file:
- /etc/minha-api/minha-api.env
ports:
- "127.0.0.1:3000:3000"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/healthz"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
O arquivo /etc/minha-api/minha-api.env deve ficar fora do repositório:
DATABASE_URL=postgres://app:[email protected]:5432/minha_api
RUST_LOG=info,tower_http=warn
APP_ENV=production
Nunca coloque secrets no Dockerfile, no docker-compose.yml versionado ou no histórico do shell.
systemd para Subir no Boot
Crie /etc/systemd/system/minha-api.service:
[Unit]
Description=Minha API Rust
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/minha-api
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
Depois:
sudo systemctl daemon-reload
sudo systemctl enable --now minha-api
sudo systemctl status minha-api
systemd garante que a stack volte após reboot. O restart: unless-stopped no Compose cuida de crashes do container.
Nginx como Reverse Proxy
Um bloco básico com proxy headers e timeouts:
server {
listen 80;
server_name api.exemplo.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
}
location /healthz {
proxy_pass http://127.0.0.1:3000/healthz;
access_log off;
}
}
Depois use Certbot ou seu gerenciador preferido para TLS:
sudo certbot --nginx -d api.exemplo.com
Para APIs públicas, adicione rate limit no Nginx e limite tamanho de body:
client_max_body_size 2m;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location / {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
}
Deploy e Rollback
Um deploy manual seguro pode ser apenas:
cd /opt/minha-api
docker compose pull
docker compose up -d
curl -fsS https://api.exemplo.com/healthz
Para rollback, mantenha a tag anterior no docker-compose.yml ou em um arquivo .env:
APP_IMAGE=registry.example.com/minha-api:2026-05-18
docker compose up -d
curl -fsS https://api.exemplo.com/healthz
Evite latest em produção. Tags imutáveis por commit SHA ou data facilitam auditoria e rollback.
Logs e Observabilidade
Com logs JSON via tracing, você consegue inspecionar rapidamente:
docker logs --tail=100 -f rust-api
journalctl -u minha-api -n 100 --no-pager
Para serviços em crescimento, envie logs para Loki, Datadog ou outro agregador. Métricas básicas podem começar com contador de requisições, latência p95 e erros 5xx. O artigo de logging e observabilidade em Rust mostra a base com tracing, OpenTelemetry e Prometheus.
Checklist de Produção
Antes de apontar tráfego real, confirme:
- container não roda como root;
- secrets ficam em
/etc/minha-api/*.env, cofre ou secret manager; -
/healthzresponde 200; - Nginx usa HTTPS e encaminha
X-Forwarded-*; -
RUST_LOGestá em nível adequado; - rollback foi testado pelo menos uma vez;
- backups e migrações de banco foram definidos;
- monitoramento externo alerta se
/healthzfalhar; - portas internas não estão expostas publicamente.
Erros Comuns
Rodar cargo run --release no servidor
Funciona para teste, mas mistura toolchain, código-fonte e runtime em produção. Gere imagem ou binário versionado.
Expor a porta 3000 para a internet
Prefira 127.0.0.1:3000:3000 no Compose e deixe Nginx ser a única entrada pública.
Usar latest
latest torna deploys não reprodutíveis. Use minha-api:<sha> ou minha-api:2026-05-19.
Health check que depende de terceiros
Se /healthz chama gateway de pagamento, API externa ou LLM, você transforma instabilidade externa em restart interno. Separe health básico de readiness profundo.
Conclusão
Rust já entrega binários rápidos e confiáveis; o deploy precisa preservar essas qualidades. Uma VPS com Docker, systemd e Nginx é uma base simples para produção: barata, debuggável e com rollback claro. Quando o tráfego ou a equipe crescerem, a mesma disciplina — imagem pequena, health check, logs estruturados e tags imutáveis — migra bem para Kubernetes ou plataformas gerenciadas.
Esse tipo de experiência também ajuda em vagas Rust de backend, plataforma e infraestrutura, principalmente quando você consegue explicar trade-offs de operação e não apenas sintaxe da linguagem. Se a empresa mantém serviços em várias linguagens, vale comparar o mesmo desenho com o ecossistema do Go Brasil para Docker e deploy, porque Go segue muito forte em control planes, CLIs e serviços cloud native enquanto Rust ganha espaço em componentes que pedem binário enxuto, segurança de memória e controle fino de recursos.
O próximo passo é automatizar esse fluxo com CI/CD, gerar tags por commit e publicar a imagem apenas depois de cargo test, cargo clippy e build de release passarem. Para transformar isso em portfólio, publique um repositório pequeno com README de arquitetura, Dockerfile, Compose, unidade systemd e um postmortem simulado de rollback. Depois, conecte esse projeto à sua página de empresas que usam Rust e mostre que você entende deploy, observabilidade e manutenção, não só código local.