Ubuntu Docker
O Docker roda programas dentro de containers: caixas isoladas que já trazem tudo o que o programa precisa. Com ele você sobe um servidor web, um banco de dados ou um app inteiro com um comando, sem bagunçar o seu sistema, e apaga tudo com outro comando.
- A diferença entre container, imagem e máquina virtual.
- As duas formas de instalar o Docker no Ubuntu, e qual escolher.
- O que cada parte de
docker run -d -p 8080:80 --name web nginxfaz. - Como guardar dados fora do container com volumes.
- Como criar a sua própria imagem com um
Dockerfile. - O que fazer com "permission denied", "port is already allocated" e container que morre na hora.
Os exemplos acontecem na máquina fictícia notebook, usuária ana. As saídas do Docker mudam um pouco entre versões (IDs, tamanhos, números de versão): trate-as como ilustrativas.
Conceito: imagem, container e VM
Três palavras aparecem o tempo todo. Pense numa receita de bolo:
| Palavra | Analogia | O que é de verdade |
|---|---|---|
| Imagem | A receita | Um pacote somente-leitura com o programa e tudo de que ele precisa (bibliotecas, configurações). Ex.: nginx, postgres, ubuntu. |
| Container | O bolo feito | Uma imagem rodando. Da mesma imagem saem quantos containers você quiser. |
| Registro | O livro de receitas | Onde as imagens ficam guardadas. O padrão é o Docker Hub. |
E qual a diferença para uma máquina virtual (veja KVM)?
| Container (Docker) | Máquina virtual (KVM, VirtualBox) | |
|---|---|---|
| Kernel | Usa o kernel do próprio Ubuntu | Tem kernel próprio |
| Sobe em | Menos de 1 segundo | Dezenas de segundos |
| Tamanho típico | Dezenas a centenas de MB | Vários GB |
| Roda outro sistema? | Só Linux (outra distribuição, sim) | Qualquer um, até Windows |
| Isolamento | Bom, mas compartilha o kernel | Mais forte |
| Melhor para | Rodar apps e serviços | Rodar um sistema inteiro |
Instalar: duas formas
Existem dois pacotes diferentes, de fontes diferentes. Os dois funcionam; não instale os dois juntos.
docker.io (do Ubuntu) | docker-ce (oficial da Docker Inc.) | |
|---|---|---|
| De onde vem | Repositório do próprio Ubuntu | Repositório da Docker (download.docker.com) |
| Instalação | Um apt install | Adicionar chave e repositório antes |
| Versão | Um pouco mais antiga, testada pelo Ubuntu | Sempre a mais nova |
| Atualizações de segurança | Pelo Ubuntu, junto com o sistema | Pela Docker |
| Compose | Pacote docker-compose-v2 | Pacote docker-compose-plugin |
| Para quem | Quem está aprendendo e a maioria dos servidores | Quem precisa de um recurso recente |
Forma 1: pacote do Ubuntu (recomendada para começar)
- sudo
- Instalar programas exige administrador.
- apt install
- Instala pacotes (veja apt).
- docker.io
- O motor do Docker (o daemon) e o comando
docker. - docker-compose-v2
- O comando
docker compose, para vários containers (veja Docker Compose).
$ sudo apt update $ sudo apt install docker.io docker-compose-v2 [sudo: authenticate] Senha: … Configurando docker.io … Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service. $ systemctl is-active docker active « o serviço já está rodando $ docker --version Docker version 28.2.2, build 28.2.2-0ubuntu1 « número ilustrativo
Forma 2: repositório oficial da Docker
Siga sempre o passo a passo atual em docs.docker.com/engine/install/ubuntu. Em resumo: remova pacotes antigos, adicione a chave em /etc/apt/keyrings, adicione o repositório (veja Repositórios e PPAs) e instale:
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Usar sem sudo: o grupo docker
O comando docker conversa com o serviço por um arquivo especial, o socket /var/run/docker.sock. Só o root e quem está no grupo docker podem usá-lo. Sem isso, você vê:
$ docker ps permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.47/containers/json": dial unix /var/run/docker.sock: connect: permission denied
- sudo usermod
- Altera uma conta de usuário (veja Usuários e Grupos).
- -aG docker
-a= acrescentar,-G docker= ao grupo docker. Sem o-a, você sairia dos outros grupos (inclusive dosudo).- $USER
- Variável com o seu nome de usuário (
ana).
$ sudo usermod -aG docker $USER $ groups ana adm cdrom sudo dip plugdev users lpadmin « docker ainda não aparece $ newgrp docker $ groups docker adm cdrom sudo dip plugdev users lpadmin ana $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES « funcionou (lista vazia)
O grupo novo só vale em sessões novas. O newgrp docker resolve no terminal atual; para valer em tudo, saia da sessão e entre de novo.
Quem está no grupo docker pode montar o disco inteiro dentro de um container e mexer em qualquer arquivo do sistema. Em servidores com várias pessoas, coloque no grupo só quem já teria sudo. Uma alternativa mais segura é o modo rootless do Docker.
O primeiro container: hello-world
- docker
- O programa cliente: ele só pede coisas ao serviço (daemon).
- run
- Subcomando: criar um container a partir de uma imagem e rodá-lo.
- hello-world
- O nome da imagem. Sem
:versão, o Docker usa:latest.
$ docker run hello-world
Unable to find image 'hello-world:latest' locally « não tinha a imagem aqui…
latest: Pulling from library/hello-world « …então baixou do Docker Hub
e6590344b1a5: Pull complete
Digest: sha256:ec153840d1e635ac434fab5e377081f17e0e15afab27beb3f726c3265039cfff
Status: Downloaded newer image for hello-world:latest
Hello from Docker! « daqui para baixo é o programa rodando
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
…
Leia a saída em duas partes. As 5 primeiras linhas são o Docker baixando a imagem, o que só acontece na primeira vez. O resto é o programa de dentro do container, que imprime a mensagem e termina. Quando o programa termina, o container para.
O que o comando devolve
docker run devolve o código de saída do programa de dentro do container. Isso permite usar containers em scripts:
$ docker run --rm ubuntu true $ echo $? 0 $ docker run --rm ubuntu false $ echo $? 1 « o "false" de dentro do container falhou $ docker run --rm ubuntu comando-que-nao-existe docker: Error response from daemon: failed to create task for container: … exec: "comando-que-nao-existe": executable file not found in $PATH: unknown $ echo $? 127
Um servidor de verdade: nginx
Agora um container que fica rodando: o servidor web nginx. É aqui que entram as opções mais usadas.
- docker run
- Cria e roda um container.
- -d
- detached, "desacoplado": roda em segundo plano e devolve o terminal.
- -p 8080:80
- publish: a porta 8080 do Ubuntu leva à porta 80 do container. Formato
host:container. - --name web
- Um nome fácil. Sem ele, o Docker inventa um (ex.:
quirky_lovelace). - nginx
- A imagem.
$ docker run -d -p 8080:80 --name web nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx … Status: Downloaded newer image for nginx:latest 9b1f2c7a0d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a « o ID completo do container novo $ curl -s localhost:8080 | head -4 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> « o nginx respondeu pela porta 8080
Abra http://localhost:8080 no navegador e você verá a página "Welcome to nginx!".
Ver os containers: docker ps
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9b1f2c7a0d3e nginx "/docker-entrypoint.…" 12 seconds ago Up 11 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp web
| Coluna | Significado |
|---|---|
| CONTAINER ID | Os 12 primeiros caracteres do ID. Você pode usá-lo no lugar do nome. |
| IMAGE | De qual imagem o container nasceu. |
| COMMAND | O programa que roda dentro (cortado com "…"). |
| CREATED / STATUS | Quando foi criado e se está Up (rodando) ou Exited (parado). |
| PORTS | 0.0.0.0:8080->80/tcp = porta 8080 de todas as interfaces do Ubuntu → porta 80 do container. |
| NAMES | O nome que você deu com --name. |
docker ps mostra só os que estão rodando. docker ps -a (de all) mostra também os parados.
Logs, entrar no container, parar e apagar
$ docker logs --tail 2 web 172.17.0.1 - - [15/Oct/2026:13:30:02 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.14.1" "-" 172.17.0.1 - - [15/Oct/2026:13:30:09 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "-" "Mozilla/5.0 …" "-" $ docker exec -it web bash
- docker exec
- Roda um comando dentro de um container que já está rodando.
- -it
-imantém a entrada aberta e-tcria um terminal: juntos, deixam você digitar lá dentro.- web
- O container.
- bash
- O programa a rodar: um shell.
Agora o prompt muda: você está dentro do container, como root dele (não do Ubuntu).
#> cat /etc/os-release | head -1 PRETTY_NAME="Debian GNU/Linux 13 (trixie)" « a imagem do nginx é baseada em Debian #> ls /usr/share/nginx/html 50x.html index.html #> exit exit
$ docker stop web web $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9b1f2c7a0d3e nginx "/docker-entrypoint.…" 5 minutes ago Exited (0) 3 seconds ago web $ docker start web web $ docker rm -f web web « -f para e apaga de uma vez
--rm
Com docker run --rm, o container é apagado sozinho quando termina. Use para testes rápidos, como docker run --rm -it ubuntu:26.04 bash, que abre um Ubuntu limpo para brincar.
Imagens
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest a830707172e8 2 weeks ago 192MB ubuntu 26.04 4b1d0c4a2c3f 3 weeks ago 78.1MB hello-world latest 74cc54e27dc4 9 months ago 10.1kB $ docker pull postgres:17 $ docker rmi hello-world Untagged: hello-world:latest Deleted: sha256:74cc54e27dc4… $ docker image prune WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] y Total reclaimed space: 0B
A tag é a versão: postgres:17, ubuntu:26.04. Em servidores, fixe a tag. Com latest, a versão muda sozinha quando você baixa de novo, e isso pode quebrar o app.
Guardar dados: volumes
Tudo o que um programa grava dentro do container some quando o container é apagado. Para guardar dados, ligue uma pasta de fora a uma pasta de dentro. Há duas formas:
Forma 1: pasta do Ubuntu (bind mount)
- -v ~/site:/usr/share/nginx/html
pasta-do-ubuntu:pasta-do-container. O que você muda em~/siteaparece na hora no container.- :ro
- Opcional: read-only, o container só pode ler.
$ mkdir ~/site $ echo "<h1>Site da Ana</h1>" > ~/site/index.html $ docker run -d -p 8080:80 --name web -v ~/site:/usr/share/nginx/html:ro nginx $ curl localhost:8080 <h1>Site da Ana</h1> « o nginx serve o arquivo do seu Ubuntu
Forma 2: volume gerenciado pelo Docker
$ docker volume create dados-db dados-db $ docker run -d --name db -e POSTGRES_PASSWORD=segredo -v dados-db:/var/lib/postgresql/data postgres:17 $ docker volume ls DRIVER VOLUME NAME local dados-db
| Forma | Exemplo | Melhor quando… |
|---|---|---|
| Pasta do Ubuntu | -v ~/site:/dados | Você quer editar os arquivos (código, site, configurações). |
| Volume nomeado | -v dados-db:/dados | Os dados são do programa (banco de dados) e você não mexe à mão. |
| Sem volume | (nada) | Os dados podem sumir (testes). |
Sua própria imagem: Dockerfile
Um Dockerfile é a receita da imagem: um arquivo de texto com uma instrução por linha. Exemplo: um site estático servido pelo nginx.
# Parte de uma imagem pronta (a "base") FROM nginx:1.29-alpine # Copia os arquivos do projeto para dentro da imagem COPY site/ /usr/share/nginx/html/ # Documenta a porta que o programa usa EXPOSE 80 # (o nginx já define o comando de partida; não é preciso CMD aqui)
| Instrução | O que faz |
|---|---|
FROM | Imagem base. Toda receita começa daqui. |
RUN | Roda um comando durante a construção (ex.: RUN apt-get update && apt-get install -y curl). |
COPY | Copia arquivos do projeto para dentro da imagem. |
WORKDIR | Define a pasta de trabalho dentro da imagem. |
ENV | Define variáveis de ambiente. |
EXPOSE | Documenta a porta. Não publica nada: isso é o -p. |
CMD | O comando que roda quando o container sobe. |
- docker build
- Constrói uma imagem a partir de um Dockerfile.
- -t meusite:1.0
- tag: o nome e a versão da imagem nova.
- .
- O "contexto": a pasta atual, onde estão o
Dockerfilee a pastasite/.
$ ls Dockerfile site $ docker build -t meusite:1.0 . [+] Building 3.2s (7/7) FINISHED => [internal] load build definition from Dockerfile => [1/2] FROM docker.io/library/nginx:1.29-alpine => [2/2] COPY site/ /usr/share/nginx/html/ => exporting to image => => naming to docker.io/library/meusite:1.0 $ docker run -d -p 8081:80 --name meusite meusite:1.0
Formas de manter um app rodando
| Forma | Exemplo | Melhor quando… |
|---|---|---|
docker run à mão | docker run -d … | Testes e um container só. |
| Política de reinício | docker run -d --restart unless-stopped … | Um container que deve voltar sozinho depois de um reboot. |
| Docker Compose | docker compose up -d | Vários containers que trabalham juntos (site + banco). Veja Docker Compose. |
| Sem Docker | apt install nginx + systemctl | Um serviço simples que o Ubuntu já empacota. |
Quando não funciona
O que aconteceu: seu usuário não está no grupo docker, ou entrou nele e ainda não abriu uma sessão nova.
Como confirmar: groups não mostra docker.
Como resolver: sudo usermod -aG docker $USER e depois saia e entre na sessão (ou use newgrp docker).
$ docker ps Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? $ systemctl is-active docker inactive
Como resolver: sudo systemctl enable --now docker. Se não subir, veja o motivo em journalctl -u docker -n 30 (veja Logs).
$ docker run -d -p 8080:80 --name web2 nginx docker: Error response from daemon: driver failed programming external connectivity on endpoint web2 (3c1e…): Bind for 0.0.0.0:8080 failed: port is already allocated.
O que aconteceu: outro container (ou outro programa) já usa a porta 8080 do Ubuntu.
Como confirmar: docker ps e sudo ss -tulpn | grep 8080 (veja ss).
Como resolver: escolha outra porta do lado do Ubuntu (-p 8081:80) ou pare o outro container. O container web2 foi criado mesmo assim, parado: apague com docker rm web2.
$ docker run -d --name teste ubuntu 5d2a… $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES « vazio! $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5d2a8f1e7c33 ubuntu "/bin/bash" 10 seconds ago Exited (0) 9 seconds ago teste
O que aconteceu: um container vive enquanto o programa principal dele vive. A imagem ubuntu roda bash, que sem terminal termina na hora.
Como resolver: para usar interativamente, rode com -it: docker run -it ubuntu. Se for um app seu que morre, leia o motivo com docker logs teste.
$ docker run nginxx Unable to find image 'nginxx:latest' locally docker: Error response from daemon: pull access denied for nginxx, repository does not exist or may require 'docker login'.
O que aconteceu: erro de digitação no nome da imagem, ou uma tag que não existe. Confira o nome no Docker Hub.
Referência
Comandos
| Comando | O que faz |
|---|---|
docker run IMAGEM | Cria e roda um container |
docker ps / ps -a | Lista containers rodando / todos |
docker logs [-f] NOME | Mostra a saída do container (-f acompanha) |
docker exec -it NOME bash | Abre um shell dentro do container |
docker stop / start / restart NOME | Para, inicia, reinicia |
docker rm [-f] NOME | Apaga o container |
docker images | Lista imagens baixadas |
docker pull IMAGEM:TAG | Baixa uma imagem |
docker rmi IMAGEM | Apaga uma imagem |
docker build -t NOME . | Constrói uma imagem do Dockerfile |
docker volume ls / create / rm | Gerencia volumes |
docker system df | Quanto espaço o Docker ocupa |
docker system prune | Apaga containers parados, redes e imagens soltas |
Opções do docker run
| Opção | Descrição | Exemplo |
|---|---|---|
-d | Segundo plano | docker run -d nginx |
-it | Terminal interativo | docker run -it ubuntu bash |
-p H:C | Publica a porta C do container na porta H do Ubuntu | -p 8080:80 |
-v ORIGEM:DESTINO | Liga pasta ou volume | -v ~/site:/dados |
-e VAR=valor | Variável de ambiente | -e TZ=America/Sao_Paulo |
--name | Dá um nome | --name web |
--rm | Apaga ao terminar | docker run --rm … |
--restart | no, always, unless-stopped, on-failure | --restart unless-stopped |
Portas publicadas com -p passam por regras próprias do Docker e podem ficar acessíveis mesmo com o ufw bloqueando. Para publicar só para a própria máquina, use -p 127.0.0.1:8080:80.
Exercícios
1. Porta
Complete para que http://localhost:9000 mostre o nginx (que escuta na porta 80 do container):
2. Entrar no container
Complete para abrir um shell dentro do container web:
3. Ver os parados
Complete para listar também os containers parados:
4. Você apagou o container do banco com docker rm -f db. Em qual caso os dados continuam existindo?
5. Por que estar no grupo docker é delicado?
Resumo
- Imagem é a receita, container é a imagem rodando.
- Instale com
sudo apt install docker.io docker-compose-v2e entre no grupodocker, sabendo que ele equivale a root. docker run -d -p HOST:CONTAINER --name NOME IMAGEMé a forma mais usada.ps,logs,exec -it,stop,rmsão o dia a dia.- Dados importantes vão em volumes (
-v); sem isso, somem com o container. - Um
Dockerfiledescreve a sua própria imagem, edocker build -ta constrói.