Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Diferenciar variáveis de build e de runtime no Next.js, no Vite e no Docker
- Decidir quais valores podem ser públicos e quais precisam ficar no servidor
- Manter segredos fora dos bundles e das camadas de imagem Docker
Nesta página
- Qual a diferença entre variáveis de build e de runtime?
- Quais variáveis são de build no Next.js?
- Quais variáveis são de build no Vite e em outros bundlers?
- Como variáveis de build e de runtime funcionam no Docker?
- Como decidir onde cada valor fica?
- Por que uma variável alterada não faz efeito?
- Como fazer isso na Square Cloud
- Perguntas frequentes
Você define API_URL no painel, reinicia o app e o site continua chamando o endereço antigo. Ou um segredo que você achava privado aparece na aba de rede do navegador. Os dois problemas têm a mesma causa: a variável foi lida num momento diferente do que você imaginava.
Uma variável pode ser consumida em dois momentos. No tempo de build, uma ferramenta como Next.js, Vite ou Docker lê o valor enquanto gera os arquivos finais. No tempo de execução (runtime), o processo em execução lê o valor do seu ambiente. O momento da leitura decide se uma mudança pede restart ou novo build, e se o valor pode vazar.
Qual a diferença entre variáveis de build e de runtime?#
Uma variável de build é lida uma vez, durante o build, e em geral escrita nos arquivos gerados como texto puro. Depois disso, quem manda é o arquivo, não o ambiente.
Uma variável de runtime é consultada toda vez que o processo inicia, por process.env no Node.js ou os.environ no Python. Você muda o valor, reinicia e o app enxerga o novo. É o modelo da regra de configuração do twelve-factor: a configuração mora em variáveis de ambiente, porque ela muda entre deploys e o código não.
| Tempo de build | Runtime | |
|---|---|---|
| Quem lê | Bundler, compilador, build do Docker | O seu processo em execução |
| Mudança exige | Novo build | Restart |
| Onde o valor termina | Dentro dos arquivos gerados | Só na memória do processo |
| Seguro para segredos | Só se nunca chegar aos arquivos gerados | Sim, se você nunca o registrar em log nem o expuser |
Quais variáveis são de build no Next.js?#
No Next.js, tudo que começa com NEXT_PUBLIC_ é de build. A documentação diz que o Next.js embute o valor no bundle JavaScript durante o next build, trocando cada referência process.env.NEXT_PUBLIC_* por uma string fixa. Depois do build, o app não reage mais a mudanças nessas variáveis, e uma mesma imagem Docker promovida para vários ambientes mantém o valor do build.
Variáveis sem prefixo ficam no servidor. Se você as lê durante a renderização dinâmica, elas são avaliadas em runtime:
// app/page.tsx
import { connection } from "next/server";
export default async function Page() {
await connection(); // ativa a renderização dinâmica: avaliado a cada requisição
const apiUrl = process.env.API_URL; // runtime, só no servidor
return <p>Using {apiUrl}</p>;
}Leituras dinâmicas como process.env[name] não são embutidas, então nunca chegam ao navegador. Se o navegador precisa de um valor de runtime, exponha-o por uma rota de API sua em vez de uma variável NEXT_PUBLIC_.
Quais variáveis são de build no Vite e em outros bundlers?#
O Vite substitui os valores de import.meta.env de forma estática no build. Só variáveis que começam com VITE_ chegam ao código do cliente, e a documentação avisa que elas não devem conter informação sensível, como chaves de API, porque vão para dentro do código empacotado.
// src/main.js
const api = import.meta.env.VITE_API_URL; // congelada no bundle durante o build
const dbPassword = import.meta.env.DB_PASSWORD; // undefined no código do clienteO Vite também dá prioridade às variáveis que já existem no ambiente sobre os arquivos .env, então VITE_API_URL=https://api.example.com npm run build vence um arquivo .env. Outros bundlers seguem a mesma ideia com o próprio prefixo: o prefixo significa "copie isto para o bundle do navegador no build".
Um servidor Node.js ou Python simples não tem etapa de build, então tudo que ele lê é runtime.
Como variáveis de build e de runtime funcionam no Docker?#
O Docker acrescenta uma terceira pergunta: o valor termina numa camada da imagem? ARG existe durante o build e ENV persiste na imagem, mas a documentação do Docker diz que argumentos de build e variáveis de ambiente não devem carregar segredos, porque ficam na imagem final.
Para um segredo de que o build realmente precisa (um token de registro privado de pacotes, por exemplo), use um secret mount. Ele é montado como arquivo em /run/secrets/<id> (ou exposto como variável de ambiente com env=) só para aquela instrução e não fica salvo em nenhuma camada:
#syntax=docker/dockerfile:1
FROM node:lts-slim AS build
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN npm ci
COPY . .
ARG VITE_API_URL
RUN npm run build
FROM node:lts-slim
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
CMD ["node", "dist/server.js"]Aqui o npm_token existe só durante aquele RUN (o seu .npmrc precisa ler ${NPM_TOKEN}), VITE_API_URL é um valor público embutido de propósito e DATABASE_URL fica fora da imagem de propósito: ela é passada quando o container inicia.
docker build --secret id=npm_token,env=NPM_TOKEN \
--build-arg VITE_API_URL=https://api.example.com -t my-app .
docker run -e DATABASE_URL="$DATABASE_URL" my-appO build em múltiplos estágios também ajuda: a imagem final copia só os arquivos que você escolhe do estágio de build, não as camadas nem os argumentos dele. Veja mais em como dockerizar uma aplicação.
Como decidir onde cada valor fica?#
Faça três perguntas, nesta ordem:
- Faria mal se qualquer visitante visse? Se sim (URL do banco, segredo de API, token de bot), precisa ser uma variável de runtime, só no servidor. Nunca coloque prefixo.
- O navegador precisa dela? Então ela é pública por definição. Um prefixo de build serve para valores como um ID de analytics ou o endereço de uma API pública.
- Você precisa mudá-la sem novo build? Então não a embuta. Leia no servidor ao iniciar, ou sirva por um pequeno endpoint de configuração.
Uma armadilha frequente: um segredo funciona na sua máquina porque o seu .env o contém, e depois vaza porque alguém o renomeou para VITE_SECRET só para fazer sumir um erro de undefined. Se o framework diz que uma variável é undefined no navegador, é a proteção funcionando.
Por que uma variável alterada não faz efeito?#
Siga esta lista:
- O processo iniciou antes da mudança. Variáveis de runtime são lidas no início; reinicie o app.
- O valor foi embutido no build. Procure nos arquivos gerados com
grep -r "old-value" .next dist build. Se aparecer, gere um novo build. - O build rodou sem a variável. Confira se ela existe onde o build roda, que pode ser outro lugar além de onde o app roda. Veja como ler erros de build e deploy com GitHub Actions, onde as variáveis de build ficam nas configurações do CI.
- Tudo é string.
"false"é verdadeiro numa condição; converta o valor no código.
Como fazer isso na Square Cloud#
Na Square Cloud, você define as variáveis de ambiente por aplicação no painel, na API, na CLI ou na extensão do VS Code. O painel também importa um arquivo .env e exporta no formato KEY=VALUE. A mudança vale no próximo restart, sem novo build, o que bate com o modelo de runtime descrito acima. O seu app pode ler SQUARECLOUD_APP_ID em runtime para saber o ID da própria aplicação.
Por isso, segredos como URLs de banco e tokens ficam nessas variáveis de runtime, não no repositório nem no bundle. Não há etapa de build na plataforma: no início ela só instala as dependências (npm install quando não existe node_modules), e as suas variáveis já existem nesse momento. Os valores que o seu bundler embute (NEXT_PUBLIC_, VITE_) precisam estar certos no job de CI onde você roda o build, como o GitHub Actions, ou você pode colocar o build no comando de início, onde as variáveis já estão definidas.
Os detalhes estão no guia de variáveis de ambiente.
Deploy
Envie pelo GitHub, pela CLI ou em ZIP e publique em segundos
Perguntas frequentes
Por que minha variável mostra o valor antigo depois que eu mudei no painel?
Se o framework embutiu a variável no build (NEXT_PUBLIC_ no Next.js, VITE_ no Vite), o valor antigo já está escrito nos arquivos JavaScript. Mudar o ambiente não adianta até você gerar um novo build.
Posso colocar um segredo de API numa variável NEXT_PUBLIC_ ou VITE_?
Não. Essas variáveis são copiadas para o JavaScript enviado a todo visitante, e qualquer pessoa consegue lê-las no navegador. Guarde segredos em variáveis sem prefixo, lidas só no servidor.
ARG do Docker é seguro para senhas?
Não. A documentação do Docker avisa que argumentos de build e variáveis de ambiente ficam na imagem final. Use um secret mount (RUN --mount=type=secret) para segredos do build e passe os segredos de runtime quando o container iniciar.
Apps Node.js e Python sem bundler têm variáveis de build?
Só se você adicionar uma etapa de build. Um app interpretado sem bundler lê process.env ou os.environ quando inicia, então toda variável é de runtime.
Mais em Deploy
Ver todos os 33 artigos- O que são variáveis de ambiente (e por que usar)Variáveis de ambiente são pares chave-valor que o sistema entrega ao processo. Veja como ler no Node.js e Python e por que tokens não ficam no código.
- O que é deploy (e o que acontece quando você faz um)Deploy é o processo de levar o código de uma aplicação para um servidor, prepará-lo e colocá-lo no ar. Veja as etapas, os métodos e como voltar atrás.
- Quanta RAM e CPU minha aplicação precisa?Dimensione RAM e CPU medindo o pico real da aplicação e somando uma folga de 30%. Veja como medir em Node.js, Python e Java e o que fazer quando faltar.
- Deploy automático com GitHub ActionsDeploy automático com GitHub Actions envia seu código para o servidor a cada push. Veja o workflow passo a passo, secrets, testes antes do deploy e erros comuns.