Persista dados com volumes Docker
Pare de perder dados quando um container é removido. Aprenda os três tipos de armazenamento do Docker (volumes nomeados, bind mounts e tmpfs) persistindo um banco PostgreSQL, inspecionando um volume, editando arquivos ao vivo por um bind mount e usando volumes no Compose.
Problema
Você roda um banco de dados num container, insere linhas importantes, depois remove o container para atualizá-lo — e cada linha some. Isso não é um bug: a camada gravável de um container é **efêmera**. Ela nasce e morre com o container. Quando você faz `docker rm` num container, sua camada gravável — e tudo que seu app escreveu ali — é apagada permanentemente. Tudo bem para um servidor web sem estado, mas catastrófico para um banco de dados, arquivos enviados ou qualquer estado que te importe. A solução é guardar os dados **fora** do ciclo de vida do container, num armazenamento que o Docker gerencia à parte: **volumes nomeados** (gerenciados pelo Docker, a escolha preferida), **bind mounts** (uma pasta do seu host, ótima para desenvolvimento) e **tmpfs** (em memória, para dados sensíveis ou descartáveis). Neste lab você vai provar que o problema é real — escrever dados, destruir o container e ver um *novo* container encontrar os mesmos dados ainda lá. Você vai inspecionar onde o Docker guarda um volume no disco, editar um site ao vivo por um bind mount e declarar volumes num `docker-compose.yml`. No fim você vai saber exatamente qual tipo de armazenamento usar e por quê.
Objetivos
- Explicar por que a camada gravável de um container é efêmera e o que se perde no
docker rm - Escolher corretamente entre volumes nomeados, bind mounts e tmpfs para um dado caso de uso
- Criar um volume nomeado, montá-lo num container e provar que os dados sobrevivem à remoção do container
- Inspecionar um volume com
docker volume lsedocker volume inspect, lendo seu Mountpoint e driver - Montar uma pasta do host como bind mount e editar arquivos que refletem ao vivo dentro de um container em execução
- Declarar um volume nomeado e um bind mount no
docker-compose.yml, e fazer backup de um volume comtar
Pré-requisitos
- Docker instalado e rodando (Docker Desktop no Windows/macOS, ou Docker Engine no Linux). Rode
docker version— se imprimir uma seção Client e uma Server, você está pronto. - Docker Compose disponível (
docker compose version— já vem no Docker Desktop e no Docker Engine moderno) - Um terminal (PowerShell, bash ou zsh) e um navegador
- Acesso à internet para alcançar o Docker Hub nos downloads de imagem
- Git instalado, para entregar seu deliverable no fim
- Você já fez os labs anteriores de Docker (containers, imagens, Compose, redes) — este é o Lab 5, o último grátis
O que você vai construir
Você não vai escrever muito código de aplicação neste lab. Em vez disso, vai rodar um banco PostgreSQL
real e um servidor web nginx, e anexar armazenamento que sobrevive ao container. Você vai escrever
dados, destruir o container e ver um container novinho encontrar os dados intactos — a prova de que
volumes funcionam. Depois vai inspecionar um volume no disco, editar um site ao vivo por um bind mount
e declarar armazenamento no Docker Compose.
O modelo mental essencial: a camada do container é efêmera
Todo container ganha uma fina camada gravável por cima de sua imagem somente-leitura. Tudo que o
app escreve — arquivos de banco, uploads, logs — cai ali por padrão. O problema:
Quando você remove o container, essa camada gravável é apagada junto. Os dados somem.
Isso é por design: containers foram feitos para serem descartáveis. Então os dados persistentes precisam
viver fora do container, num armazenamento que o Docker gerencia de forma independente. O Docker te
dá três opções:
| Tipo | Onde vive | Gerenciado por | Melhor para |
|---|---|---|---|
| Volume nomeado | Área própria do Docker no host (/var/lib/docker/volumes/) |
Docker | Bancos de dados, dados persistentes do app — a escolha padrão |
| Bind mount | Uma pasta específica que você escolhe no host | Você | Desenvolvimento: editar código/config ao vivo |
| tmpfs | RAM do host, nunca escrito em disco | Docker (em memória) | Dados sensíveis ou descartáveis (segredos, caches) |
Como escolher
- Precisa que os dados sobrevivam à remoção do container, e não liga para onde ficam no disco? →
volume nomeado. O Docker gerencia, é portátil entre containers e fácil de fazer backup. - Precisa editar arquivos no seu host e vê-los instantaneamente dentro do container (recarregar um
site ou app ao vivo em dev)? → bind mount. Você aponta para uma pasta exata do host. - Precisa de um espaço de rascunho que nunca pode tocar o disco (um segredo, um cache temporário)? →
tmpfs. Ele vive na RAM e evapora quando o container para.
Como trabalhar neste lab
Faça os passos em ordem — cada um se apoia no anterior. Rode cada comando você mesmo e leia a saída real.
No caminho você vai coletar as evidências (prova de persistência, volume inspect, edição ao vivo por
bind mount) que precisa para a entrega do Passo 6.
Passos
-
O problema: a camada gravável morre com o container
Antes de tocar em volumes, sinta a dor que eles resolvem. Você vai escrever um arquivo dentro de um
container, remover o container e confirmar que o arquivo sumiu.Prove que os dados se perdem
Rode um container Alpine descartável, escreva um arquivo nele e olhe-o:
docker run --name temp -d alpine:3.20 sh -c "echo 'dado importante' > /data.txt && sleep 3600" docker exec temp cat /data.txt # imprime: dado importanteO arquivo existe — ele vive na camada gravável do container. Agora destrua o container e tente
recuperá-lo:docker rm -f temp docker run --rm alpine:3.20 cat /data.txt # erro: arquivo não existeO novo container é uma instância nova da mesma imagem. Ele nunca viu seu
data.txt, porque esse
arquivo vivia só na camada gravável do container removido — que foi apagada junto.Os três tipos de armazenamento (e quando usar cada um)
A resposta do Docker é manter os dados fora do ciclo de vida do container. Há três mecanismos —
grave-os na memória, porque todo passo seguinte é um deles:-
Volumes nomeados — totalmente gerenciados pelo Docker, guardados na área própria do Docker no
host. A forma preferida de persistir dados. Isolados do sistema de arquivos do host, fáceis de fazer
backup, migrar e compartilhar entre containers. Use para bancos de dados e qualquer dado persistente
do app. -
Bind mounts — um arquivo ou diretório do host é montado dentro do container. Menos isolamento,
mais flexibilidade; edições de qualquer lado são visíveis no outro na hora. Use para desenvolvimento
— editar código ou config ao vivo. -
Montagens tmpfs — armazenadas na memória do host, nunca escritas em disco. Efêmeras por definição.
Use para dados sensíveis que você não quer em disco, ou dados de rascunho que não precisam sobreviver
a um reinício.
Checkpoint
- Onde o
data.txtde fato vivia? (Na camada gravável do containertemp.) - Por que o segundo container não conseguiu lê-lo? (Essa camada foi apagada no
docker rm; o novo container tem sua própria camada vazia.) - Qual tipo de armazenamento você usaria para um banco PostgreSQL? (Um volume nomeado — persistente e gerenciado pelo Docker.)
Entregável deste passo: as duas saídas — o
catbem-sucedido dentro dotemp, e o erro "arquivo não existe" depois. -
Volumes nomeados — totalmente gerenciados pelo Docker, guardados na área própria do Docker no
-
Volumes nomeados: prove que os dados sobrevivem à remoção do container
Agora resolva o problema. Você vai criar um volume nomeado, rodar um banco PostgreSQL que guarda seus
arquivos ali, adicionar dados, destruir o container e subir um novo container no mesmo volume —
com seus dados intactos.Crie um volume
docker volume create pgdatapgdataé um volume nomeado gerenciado pelo Docker. Ele existe independentemente de qualquer container.Rode o PostgreSQL usando o volume
docker run -d --name lab-postgres \ -e POSTGRES_PASSWORD=minhasenha \ -v pgdata:/var/lib/postgresql/data \ postgres:16-alpine-
-v pgdata:/var/lib/postgresql/datamonta o volume nomeadopgdatano caminho exato onde o
PostgreSQL guarda seus arquivos de banco. O formato éNOME_DO_VOLUME:CAMINHO_NO_CONTAINER. - Como o lado esquerdo é um nome simples (não um caminho começando com
/ou./), o Docker o trata
como volume nomeado, não como bind mount.
Dê alguns segundos para inicializar, então crie uma tabela e insira uma linha:
docker exec -it lab-postgres psql -U postgres -c \ "CREATE TABLE alunos (id serial, nome text); INSERT INTO alunos (nome) VALUES ('Ada');" docker exec -it lab-postgres psql -U postgres -c "SELECT * FROM alunos;"Você deve ver uma linha:
Ada. Esses dados agora vivem no volumepgdata, não na camada gravável
do container.Destrua o container — mantenha o volume
docker rm -f lab-postgres docker volume ls # pgdata continua listadoO container sumiu, mas
pgdatasobrevive. É esse o ponto todo: remover um container não remove seus
volumes nomeados.Suba um NOVO container no mesmo volume
docker run -d --name lab-postgres-2 \ -e POSTGRES_PASSWORD=minhasenha \ -v pgdata:/var/lib/postgresql/data \ postgres:16-alpine docker exec -it lab-postgres-2 psql -U postgres -c "SELECT * FROM alunos;"A linha
Adaainda está lá — lida por um container completamente diferente. Isso é persistência:
os dados sobreviveram ao container que os criou.Entregável deste passo: a saída de
SELECT * FROM alunos;dolab-postgres-2(um container diferente) mostrando que a linha sobreviveu. -
-
Inspecione um volume: ls, inspect, Mountpoint
Um volume não é uma caixa-preta. O Docker te dá comandos para listar volumes e ver exatamente onde e
como cada um é armazenado.Liste os volumes disponíveis
docker volume lsIsso mostra cada volume, com seu driver (geralmente
local) e nome. Você deve verpgdata
na lista.Inspecione um volume específico
docker volume inspect pgdataA saída é JSON. Os campos que importam:
-
Name — o nome do volume (
pgdata). -
Driver — o driver do volume;
localpara volumes padrão. -
Mountpoint — o caminho no host onde os dados do volume de fato vivem (ex.:
/var/lib/docker/volumes/pgdata/_data). Essa é a área gerenciada pelo próprio Docker — em geral
você não mexe nela diretamente. - Labels — quaisquer rótulos atribuídos ao volume.
-
Scope —
local(este host) ouglobal.
O que o Mountpoint te diz
O
Mountpointé onde o Docker guarda os bytes. No Docker Desktop (Windows/macOS) esse caminho vive
dentro da VM Linux do Docker, então você não vai achá-lo no seu explorador de arquivos normal — isso
é esperado e correto. Inspecionar o Mountpoint é útil para:- confirmar onde os dados de um volume estão armazenados,
- diagnosticar problemas relacionados a armazenamento,
- reunir informações para backup ou migração (você vai usar isso no Passo 5).
Um cuidado do manual: editar arquivos diretamente no Mountpoint do host pode corromper dados que um
container em execução está usando. Deixe o container (ou um comando de backup) ser quem toca neles.Entregável deste passo: a saída de
docker volume inspect pgdata, com a linha doMountpointdestacada. -
Name — o nome do volume (
-
Bind mounts: edite um arquivo no host, veja ao vivo no container
Um volume nomeado é gerenciado pelo Docker e vive na área própria do Docker. Um bind mount é
diferente: você aponta o container para uma pasta exata do seu host, e os dois lados veem os
mesmos arquivos em tempo real. Essa é a ferramenta para desenvolvimento local.Crie uma pasta de site no host
mkdir -p lab-volume/site echo "<h1>Versão 1 — servida de um bind mount</h1>" > lab-volume/site/index.htmlSirva com nginx via bind mount
docker run -d --name site -p 8080:80 \ -v "$(pwd)/lab-volume/site:/usr/share/nginx/html:ro" \ nginx:1.27-alpine- O lado esquerdo do
-vagora é um caminho ($(pwd)/lab-volume/site), então o Docker o trata
como bind mount, não como volume nomeado. - Ele é montado na raiz web do nginx,
/usr/share/nginx/html. -
:romonta somente-leitura dentro do container — o nginx só precisa ler os arquivos. Tirar o
:rodeixaria o container escrever de volta na sua pasta do host.
No Windows PowerShell, troque
$(pwd)por${PWD}. No CMD clássico do Windows, use%cd%.Abra http://localhost:8080 — você verá "Versão 1".
Edite no host, atualize no navegador
Sem tocar no container, edite o arquivo no seu host:
echo "<h1>Versão 2 — editada ao vivo no host</h1>" > lab-volume/site/index.htmlAtualize http://localhost:8080 — agora diz "Versão 2". Você não reconstruiu nem reiniciou o
container. O bind mount faz o container ler o mesmo arquivo que você acabou de editar no host.Volume nomeado vs bind mount — a diferença prática
Volume nomeado Bind mount Localização Área gerenciada pelo Docker (você não escolhe) Um caminho exato do host que você escolhe Isolamento Alto — isolado do FS do host Baixo — ele é uma pasta do host Melhor para Bancos, dados persistentes, backups Editar código/config ao vivo em dev Portabilidade Portátil, fácil de fazer backup Preso ao layout de caminhos deste host Regra de bolso: bind mounts para desenvolvimento (você quer editar arquivos), volumes nomeados
para dados que precisam persistir (você quer que o Docker gerencie).Entregável deste passo: anote que editar o
index.htmlno host mudou a página de "Versão 1" para "Versão 2" sem reiniciar o container. - O lado esquerdo do
-
Volumes com Docker Compose + tmpfs + backup
Em projetos reais você não digita longos comandos
docker run -v— você declara o armazenamento no
docker-compose.yml, onde ele fica versionado e reproduzível. Você vai ligar um volume nomeado e um
bind mount, conhecer o tmpfs e fazer backup de um volume.Declare um volume nomeado no Compose
Em
lab-volume/, criedocker-compose.yml:services: postgres: image: postgres:16-alpine environment: POSTGRES_PASSWORD: minhasenha volumes: - pgdata:/var/lib/postgresql/data # volume nomeado (top-level, abaixo) ports: - "5432:5432" web: image: nginx:1.27-alpine volumes: - ./site:/usr/share/nginx/html:ro # bind mount (caminho do host) ports: - "8080:80" volumes: pgdata:Dois tipos de armazenamento num arquivo só:
-
pgdata:/var/lib/...— o lado esquerdo é um nome simples, então é um volume nomeado. Ele
também precisa ser declarado no blocovolumes:de nível superior, que é o que diz ao Compose para
gerenciá-lo. -
./site:/usr/share/...— o lado esquerdo é um caminho (./site), então é um bind mount para
a pasta que você criou no Passo 4.
Suba tudo, depois prove a persistência através de um teardown completo:
docker compose up -d # ... crie uma tabela / adicione dados via psql como no Passo 2 ... docker compose down # para E remove os containers docker compose up -d # containers novos, mesmo volume pgdata # ... consulte de novo: os dados continuam lá ...docker compose downremove os containers mas não o volume nomeado, então os dados sobrevivem.
(Para também apagar volumes você teria que dizerdocker compose down -v— um reset útil, e um erro
de digitação perigoso.)tmpfs — para dados sensíveis ou descartáveis
Quando os dados nunca podem tocar o disco (um segredo, um cache de rascunho), use tmpfs — ele vive
na RAM e é apagado quando o container para. No Compose:web: image: nginx:1.27-alpine tmpfs: - /tmp # em memória, some quando o container paraTudo escrito em
/tmpexiste só enquanto o container roda; pare-o e os dados são descartados. Isso é
uma vantagem para segredos e caches, não um lugar para nada que você precise guardar.Faça backup de um volume
Como um volume nomeado é só um diretório que o Docker gerencia, você faz backup empacotando (tar) seu
conteúdo a partir de um container descartável:docker run --rm \ -v pgdata:/data \ -v "$(pwd):/backup" \ alpine:3.20 tar czf /backup/pgdata-backup.tar.gz -C /data .Isso monta o
pgdataem/data, monta sua pasta atual do host em/backupe escreve
pgdata-backup.tar.gzno seu host. Para restaurar, você rodaria o inverso: extrair o tar num volume
novo. Esse truque de tar-através-de-container é a forma padrão de fazer backup e migrar volumes Docker.Notas de segurança
-
Bind mounts expõem uma pasta real do host ao container — cuide das permissões de arquivo, e
prefira:roquando o container só precisa ler (como no site nginx acima). -
Dados sensíveis pertencem ao tmpfs (em memória) ou atrás de encriptação apropriada — não num
bind mount comum em disco. - Use volumes separados para serviços separados, a menos que eles realmente precisem compartilhar dados.
Entregável deste passo: seu
docker-compose.yml, e a prova de que os dados sobreviveram a um ciclodocker compose down+up. -
-
Entregue: documente sua prova de persistência
Prove que você fez os dados sobreviverem ao container capturando a evidência num pequeno repositório.
Limpe primeiro
docker rm -f lab-postgres-2 site 2>/dev/null docker compose down # se você tiver Compose rodando # mantenha ou remova o volume pgdata como preferir: # docker volume rm pgdataMonte sua entrega
Crie uma pasta, inicialize o git e escreva um arquivo chamado
SUBMISSION.md. Cole as saídas reais
que coletou — não texto inventado.# Docker Lab 5 — Persista dados com volumes ## 1. O problema (camada efêmera) <cole o `cat /data.txt` bem-sucedido dentro do `temp`, depois o erro "arquivo não existe" após o `docker rm`> ## 2. Prova de persistência (volume nomeado) <cole `SELECT * FROM alunos;` do `lab-postgres-2` — um container DIFERENTE — mostrando que a linha sobreviveu> ## 3. docker volume inspect pgdata <cole o JSON; aponte a linha do Mountpoint> ## 4. Edição ao vivo por bind mount Editar `site/index.html` no host mudou a página de "Versão 1" para "Versão 2" sem reiniciar. (sim / não) ## 5. Compose + persistência através de down/up <cole seu docker-compose.yml, e a evidência de que os dados sobreviveram ao `docker compose down` e depois `up -d`> ## Reflexão (2-3 frases) Nas minhas palavras: por que a camada gravável é efêmera, e quando eu escolheria um volume nomeado vs um bind mount vs tmpfs.Entregue
git init git add SUBMISSION.md docker-compose.yml git commit -m "Docker lab 5 — persista dados com volumes" # faça push para um repo público ou crie um gist, depois entregue essa URLEntregue a URL do repositório ou gist como seu deliverable do lab.
Critério de submissão (autoverificação — sem stubs)
- O Passo 1 mostra o erro real "arquivo não existe" provando que a camada gravável é efêmera
- A prova de persistência é de um container diferente (
lab-postgres-2), não o que escreveu os dados - A saída de
docker volume inspectestá incluída, com oMountpointidentificado - Você confirmou que uma edição no host chegou ao container ao vivo pelo bind mount
- Seu
docker-compose.ymldeclara tanto um volume nomeado de nível superior quanto um bind mount - Você mostrou os dados sobrevivendo a um ciclo
docker compose down+up -d - Sua reflexão diz corretamente, nas suas palavras, quando usar volume vs bind mount vs tmpfs
- Todas as saídas são reais de comandos que você rodou — sem placeholder ou texto inventado
O que vem a seguir
Parabéns — esse foi o último lab grátis da trilha de Docker. Você já sabe rodar containers,
construir imagens, usar Compose, conectar containers por redes e persistir dados com volumes: todo o
núcleo do Docker do dia a dia.Daqui a trilha continua com conteúdo premium que te leva à produção:
- Segurança no Docker — endurecendo imagens e containers, usuários não-root, segredos, scanning.
- Docker em Produção — observabilidade com Prometheus e Grafana, healthchecks, limites de recursos.
- Trilha de Kubernetes — orquestrando containers em escala além de um único host.
Você construiu a fundação; o premium é onde você a leva à produção.