# Dockerfile: estender imagem, cache de build e Nginx no Docker

> Parta de uma imagem sua com FROM, entenda o cache do docker build, rode em segundo plano com -d e publique uma página no Nginx com -p.

- Autor: Henrylle Maia, consultor AWS
- Publicado em: 2026-10-15
- Assuntos: Containers
- URL: https://pages.formacaoaws.com.br/blog/dockerfile-estender-imagem-e-cache/
- Aula de origem: "Primeiros passos com Docker + Dockerfile: Estendendo imagens + cache + uso de imagem do dockerhub" (https://www.youtube.com/watch?v=Z_E8QlCnA_8)

O `FROM` do Dockerfile aceita qualquer imagem, inclusive uma que você mesmo construiu, e a ordem das linhas decide quanto do `docker build` vem do cache. Aqui eu monto uma imagem em cima de outra, rodo um container em segundo plano com `-d` e publico uma página HTML com a imagem oficial do Nginx e o `-p`.

> **Importante:** esta é uma aula de 5 de julho de 2022. A saída do `docker build` que aparece aqui (as etapas "Step 2/4") é a do Docker daquela época e pode estar diferente na sua versão, mas a lógica do cache é a mesma. Confira os detalhes na documentação oficial: [Docker build cache](https://docs.docker.com/build/cache/) e [referência do Dockerfile](https://docs.docker.com/reference/dockerfile/). O básico (imagem, container, `FROM`, `RUN`, `docker build -t` e `-f`) está em [O que é Docker](https://pages.formacaoaws.com.br/blog/o-que-e-docker/); aqui vai só o que muda.

## Estender uma imagem: do Ubuntu para a sua

Estender é pegar uma imagem pronta e incrementar. Partindo do Ubuntu 20.04 eu adiciono o Git, dou um nome ao resultado, `ubuntu-custom`, e passo a lançar containers dessa imagem, que já vem com coisas instaladas. Depois posso estender de novo, agora a partir do `ubuntu-custom` e não mais do Ubuntu.

O `RUN` executa os comandos na hora de construir a imagem, e o `-y` evita que o build fique parado esperando uma resposta:

```dockerfile
FROM ubuntu:20.04
RUN apt-get update -y
RUN apt-get install git -y
```

Construa e entre no container:

```bash
docker build -t ubuntu-custom .
docker run -ti ubuntu-custom
```

Dentro do container, `git --version` responde: o Git já vem na imagem. O `curl` ainda não, e esse é o gancho para o cache.

## Cache: por que a ordem das linhas importa

Acrescentei `RUN apt-get install curl -y` como quarta linha e construí de novo. As etapas 2 e 3 já tinham rodado e apareceram com **Using cache**; só a 4, a nova, executou do zero. Por isso o build ficou rápido.

![Saída do docker build: as etapas 2/4 (apt-get update) e 3/4 (instalação do git) com "Using cache"](https://pages.formacaoaws.com.br/blog/assets/posts/dockerfile-estender-imagem-e-cache/cache-using-cache.webp)

A regra vale para a linha em que você mexe: **da linha alterada em diante, o cache recomeça**. Se eu tivesse mexido na linha 3, tudo dali para baixo rodaria de novo. Por isso o Dockerfile vai do mais genérico para o mais específico, ou do que muda menos para o que muda mais. O que você altera com frequência fica embaixo.

## Passo 1: criar uma imagem a partir da sua

Agora não quero mais partir do Ubuntu 20.04. Crio um segundo arquivo, `Dockerfile-custom`, e digo que parto da imagem que acabei de construir. Para ter algo útil, clono o projeto da Bia, uma aplicação em Node e React (aqui o Git que eu coloquei na primeira imagem faz o serviço):

```dockerfile
FROM ubuntu-custom
RUN git clone https://github.com/henrylle/bia.git
```

Como o arquivo não se chama `Dockerfile`, o `-f` diz qual usar. O contexto, o ponto no fim, continua obrigatório: na primeira tentativa eu esqueci e o Docker reclamou que o `build` exige um argumento.

```bash
docker build -t ubuntu-custom-2 -f Dockerfile-custom .
docker run -ti ubuntu-custom-2
```

O `ls` mostra a pasta `bia` na raiz do container, e dentro dela estão todos os arquivos do projeto. Todo container lançado de `ubuntu-custom-2` já nasce com o Git instalado e o projeto baixado.

![Terminal dentro do container ubuntu-custom-2: o ls da raiz lista a pasta bia, e o ls dentro dela lista os arquivos do projeto](https://pages.formacaoaws.com.br/blog/assets/posts/dockerfile-estender-imagem-e-cache/bia-dentro-da-imagem.webp)

## Passo 2: rodar em segundo plano com -d

Até aqui eu uso `-ti`, que deixa o terminal ligado ao container. Ele só vive enquanto eu estou dentro. Para rodar em segundo plano existe o `-d` (detached), que não prende o terminal.

Só que, rodando `docker run -d ubuntu-custom-2`, o container morre na sequência, e o `docker ps` não lista nada. Um container só continua rodando se **algum processo o segura**. No modo `-ti` quem segura é o terminal; aqui, terminou a execução, ele acaba.

Para o teste, usei um artifício. O `tail -f` fica imprimindo o conteúdo de um arquivo, e apontando para `/dev/null` ele nunca retorna nada e fica preso no processo. Coloco como `ENTRYPOINT` no `Dockerfile-custom`, o comando que o container executa sempre que é lançado:

```dockerfile
FROM ubuntu-custom
RUN git clone https://github.com/henrylle/bia.git
ENTRYPOINT ["tail", "-f", "/dev/null"]
```

Construí de novo (as etapas anteriores vieram do cache, então foi rápido) e subi com `-d`:

```bash
docker build -t ubuntu-custom-2 -f Dockerfile-custom .
docker run -d ubuntu-custom-2
docker ps
```

![Saída do build com a etapa 3/3 ENTRYPOINT tail -f /dev/null, depois o ID do container devolvido pelo docker run -d](https://pages.formacaoaws.com.br/blog/assets/posts/dockerfile-estender-imagem-e-cache/entrypoint-tail-detached.webp)

O `docker run -d` devolve o ID do container, e agora o `docker ps` o lista rodando. Para parar, passe os primeiros caracteres do ID:

```bash
docker stop <primeiros-caracteres-do-id>
```

Esse truque é só para o exemplo. Numa imagem de verdade o `ENTRYPOINT` aponta para o processo que o container deve executar, como uma aplicação web ou um trabalho que roda e termina. Quando o processo acaba, o container acaba, e é isso que se quer: o container tem um conceito de finitude, por isso é tão usado em microsserviços.

**Fluxo:** ubuntu:20.04 (imagem do Docker Hub) → ubuntu-custom (com o git instalado) → ubuntu-custom-2 (com o projeto clonado) → Container (docker run -d)

## Passo 3: usar uma imagem pronta do Docker Hub, o Nginx

Em vez de partir de um sistema operacional, dá para partir de uma imagem que já faz o que você quer. Quero servir uma página HTML estática, então busco **nginx** no Docker Hub. O selo de imagem oficial e o número de downloads, mais de um bilhão, indicam que é a oficial. Veja a página em [hub.docker.com/_/nginx](https://hub.docker.com/_/nginx): ela traz o exemplo de Dockerfile e o comando de build.

Crio a pasta `meu_html` com um `hello.htm` ("Oi Mundo... Saí da máquina local e fui para o container") e um `Dockerfile-nginx`, que copia a pasta para o diretório que o Nginx publica:

```dockerfile
FROM nginx
COPY meu_html /usr/share/nginx/html
```

```bash
docker build -t meu-nginx -f Dockerfile-nginx .
```

Na primeira tentativa esqueci o `-f` e o Docker pegou o `Dockerfile` padrão. Com o `-f`, ele baixa o Nginx, copia o conteúdo da pasta e a imagem fica pronta.

## Passo 4: publicar a porta com -p

O container roda na minha máquina, mas para abrir a página como `localhost` preciso compartilhar uma porta dele. O Nginx responde na porta 80 dentro do container. Do lado da minha máquina eu escolho qualquer porta livre: a documentação sugere a 8080 porque a 80 pode estar ocupada, e eu usei a 8081. O `-p` mapeia as duas, `<porta-da-máquina>:<porta-do-container>`:

```bash
docker run --name meu-container-nginx -d -p 8081:80 meu-nginx
```

O `--name` dá um nome ao container; sem ele o Docker gera um nome aleatório. O `docker ps` confirma que está rodando e mostra o mapeamento. Abri `localhost:8081/hello.htm` e a página apareceu.

## Passo 5: atualizar a página exige uma imagem nova

Mudei o texto do HTML, acrescentando "(Sou a v2)". Rodar o container de novo não muda nada, porque o HTML foi copiado para dentro da imagem. O caminho é parar o container, construir a imagem outra vez e subir de novo, agora na porta 8082:

```bash
docker stop <id>
docker build -t meu-nginx -f Dockerfile-nginx .
docker run -d -p 8082:80 meu-nginx
```

A 8081 saiu do ar e a 8082 mostrou a página com a v2. Um container roda de uma imagem: para o container mudar, a imagem muda primeiro. O mesmo raciocínio aparece em [Docker na EC2](https://pages.formacaoaws.com.br/blog/docker-na-ec2/), com o Apache, e os comandos que você usa no dia a dia estão em [Comandos Docker essenciais](https://pages.formacaoaws.com.br/blog/comandos-docker-essenciais/).

## Checklist

- Parta de uma imagem sua com `FROM <nome-da-imagem>` e construa a próxima em cima dela.
- Use `-f <arquivo>` quando o Dockerfile não se chama `Dockerfile`, e não esqueça o contexto (o ponto) no fim do `docker build`.
- Ponha no Dockerfile o que muda menos nas primeiras linhas: da linha alterada em diante o cache recomeça.
- Com `docker run -d`, garanta que um processo segura o container, senão ele termina na hora.
- Pare um container com `docker stop` e os primeiros caracteres do ID.
- Para servir uma página, parta da imagem oficial do Nginx e copie o HTML para `/usr/share/nginx/html`.
- Publique a porta com `-p <porta-da-máquina>:80` e confira no `docker ps`.
- Mudou o conteúdo? Pare o container, construa a imagem de novo e suba outro.

Estender imagens, ordenar bem o Dockerfile e publicar uma porta são a base para o passo seguinte: pegar uma aplicação local, containerizar e levar para a nuvem da AWS.
