# Docker run para Docker Compose: converta um comando em arquivo

> Pegue o docker run da documentação de uma imagem e converta em docker-compose.yml: nome, porta, volumes e restart always, testado com o TeamCity na EC2.

- Autor: Henrylle Maia, consultor AWS
- Publicado em: 2026-10-15
- Assuntos: Containers
- URL: https://pages.formacaoaws.com.br/blog/docker-run-para-docker-compose/
- Aula de origem: "Migrando de docker para docker-compose na AWS" (https://www.youtube.com/watch?v=V5kXwrGe0SE)

Muita imagem no Docker Hub ensina a subir o container com um `docker run` cheio de flags. Funciona, mas é uma instrução que você precisa guardar e gerenciar do seu lado. Aqui eu converto esse comando em um `docker-compose.yml`, flag por flag, e deixo o servidor subindo sozinho quando a máquina religa.

Gravei essa aula em 10 de novembro de 2023, com o servidor de build TeamCity, da JetBrains, numa EC2. A versão do arquivo e os comandos são os daquela data: antes de copiar, confira a [documentação oficial do Compose](https://docs.docker.com/compose/) e a [referência do arquivo](https://docs.docker.com/reference/compose-file/), porque o formato pode ter mudado. Se você ainda não viu o básico do Compose, comece por [Docker Compose na prática](https://pages.formacaoaws.com.br/blog/docker-compose-na-pratica/).

## Por que sair do docker run

Num cliente em que faço consultoria, tivemos que montar um ambiente para rodar build, com o TeamCity, e as instruções de subida também estavam em `docker run`. Com o Compose, o mesmo ambiente fica num arquivo central e estruturado: você enxerga de uma vez o mapeamento de volume, a porta e tudo que o projeto precisa para rodar.

Tem ainda a rede. Ao subir com `docker run`, o Docker cria uma rede nos bastidores. Quando você sobe outro container, um banco para esse servidor de build, por exemplo, ele fica numa rede separada, e você precisa mapear isso na mão. O Compose faz esse gerenciamento, e ligar dois serviços fica bem mais simples. O arquivo também entra no versionamento do projeto.

Se você entende a conversão, entende o que está escrito no arquivo. Sem isso, o Compose só gera confusão no projeto.

## Passo 1: preparar a EC2

Fiz tudo na AWS, partindo do zero, para você ver o ambiente rodando:

- Um security group chamado `aula docker mais docker compose`, com SSH liberado só para o meu IP e HTTP (porta 80) para o mundo, porque o servidor de build sai pela porta 80.
- Um par de chaves e uma instância `t3.micro`, com disco de 12 GB.
- No Advanced Details, no User Data, colei o script que instala o Docker. Ele está no meu projeto BIA no GitHub, na pasta `scripts`.
- Conectei o VS Code à instância por SSH. Esse passo está em [Docker na AWS com VS Code Remote SSH](https://pages.formacaoaws.com.br/blog/ambiente-docker-na-aws-com-vs-code-remote-ssh/). Lembre de dar `chmod 400` na chave `.pem`.

> **Importante:** um servidor de build aberto para qualquer IP fica exposto a quem achar o endereço. Fora do laboratório, libere a porta só para os IPs que precisam acessar (ou coloque o acesso atrás de VPN ou load balancer). Veja as [regras de security group](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html).

## Passo 2: ler o docker run da imagem

Na página da imagem `jetbrains/teamcity-server` no [Docker Hub](https://hub.docker.com/r/jetbrains/teamcity-server), o fabricante manda baixar a imagem e rodar este comando:

![O docker run da página do TeamCity no Docker Hub: --name teamcity-server-instance, dois -v, um -p com a porta 8111 e a imagem jetbrains/teamcity-server](https://pages.formacaoaws.com.br/blog/assets/posts/docker-run-para-docker-compose/docker-run.webp)

Cada pedaço dele tem um lugar no Compose:

| No `docker run` | No `docker-compose.yml` |
|---|---|
| `jetbrains/teamcity-server` (a imagem, no fim) | `image` |
| `--name teamcity-server-instance` | `container_name` |
| `-p <porta-no-host>:8111` | `ports` |
| `-v ...:/data/teamcity_server/datadir` e `-v ...:/opt/teamcity/logs` | `volumes` |

## Passo 3: montar o arquivo

Crie o `docker-compose.yml` na pasta do usuário, a `/home/ec2-user`. Comecei pela versão e pelo serviço, que é um só neste exemplo:

```yaml
version: "3"
services:
  teamcity-server:
    image: jetbrains/teamcity-server
    restart: always
    container_name: teamcity-server-instance
    ports:
      - 80:8111
    volumes:
      - data_dir:/data/teamcity_server/datadir
      - logs:/opt/teamcity/logs
```

Linha a linha:

- **`image`:** a imagem que estava no fim do comando.
- **`container_name`:** o nome que o `--name` dava ao container.
- **`ports`:** `80:8111` mapeia a porta 80 da máquina para a 8111 do container. No navegador, basta o IP da instância.
- **`volumes`:** o comando tinha dois `-v`, um para os dados e outro para os logs. Dei um nome a cada um, `data_dir` e `logs`, e do lado direito vai o caminho que a imagem usa dentro do container.
- **`restart: always`:** não existia no comando da página. Se a EC2 for parada à noite para economizar e ligada no dia seguinte, o servidor sobe sem eu fazer nada.

## Passo 4: dizer onde ficam os volumes

Se você não declara os volumes, o Docker coloca os dados no caminho padrão. Eu quis saber onde ficam os meus dados, até para fazer backup. Então declarei os dois no fim do arquivo, com o driver `local` e um bind para uma pasta minha:

```yaml
volumes:
  data_dir:
    driver: local
    driver_opts:
      type: none
      device: /home/ec2-user/teamcity/datadir
      o: bind
  logs:
    driver: local
    driver_opts:
      type: none
      device: /home/ec2-user/teamcity/logs
      o: bind
```

![O fim do docker-compose.yml no VS Code: os volumes data_dir e logs com driver local, type none, device apontando para /home/ec2-user/teamcity e o bind](https://pages.formacaoaws.com.br/blog/assets/posts/docker-run-para-docker-compose/compose-volumes.webp)

O arquivo completo ficou com a versão, o serviço, a imagem, o nome, a porta, os volumes e o `restart`.

![O início do docker-compose.yml: version 3, o serviço teamcity-server com image, restart always, container_name, ports 80:8111 e os dois volumes](https://pages.formacaoaws.com.br/blog/assets/posts/docker-run-para-docker-compose/compose-topo.webp)

## Passo 5: subir e corrigir o erro das pastas

Na pasta do arquivo:

```bash
docker-compose up -d
```

Um comando só baixa a imagem e sobe o container. Na página da imagem, o `docker pull` e o `docker run` aparecem como dois passos.

Na minha primeira tentativa deu erro: falha ao montar o volume, porque eu ainda não tinha criado as pastas do `device`. Criei as três e subi de novo:

```bash
mkdir teamcity
cd teamcity
mkdir datadir
mkdir logs
```

Voltei para a pasta do arquivo e rodei `docker-compose up -d` outra vez.

O container `teamcity-server-instance` ficou de pé na porta 80. Na primeira vez o servidor demora um pouco para iniciar. Se a página não abrir, use `http://` e não `https://`: eu tentei com HTTPS e não carregou.

**Fluxo:** docker run (flags soltas num comando) → docker-compose.yml (imagem, nome, porta e volumes) → docker-compose up -d (baixa a imagem e sobe) → Reiniciar a EC2 (restart always sobe sozinho)

## Passo 6: entrar, ver o log e testar o restart

Dois comandos que o Compose facilita:

```bash
docker-compose exec teamcity-server /bin/bash
docker-compose logs
```

O primeiro abre um terminal dentro do container do servidor. O segundo mostra o log, onde vi o servidor iniciando.

Depois parei a instância, esperei ficar como parada e liguei de novo. Ao conectar e rodar `docker ps`, o servidor do TeamCity já estava no ar, sem eu subir nada. É o `restart: always` fazendo o trabalho. Na Formação mostro como automatizar a parada com uma Lambda e um agendamento, para você não pagar pela máquina à noite.

Um detalhe que apareceu: o IP público da instância mudou depois de ligar de novo, e tive que atualizar o endereço da conexão. Sem Elastic IP, isso acontece. O ideal é nem depender de IP público: a conexão por [Session Manager (SSM)](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html) usa o ID da instância, que não muda.

## Checklist

- Leia o `docker run` da imagem e separe: imagem, `--name`, `-p` e cada `-v`.
- Crie o `docker-compose.yml` com um serviço e traduza cada flag: `image`, `container_name`, `ports`, `volumes`.
- Se quer saber onde ficam os dados, declare os volumes no fim do arquivo, com `driver: local` e `o: bind`.
- Crie na máquina as pastas que o `device` aponta antes de subir.
- Acrescente `restart: always` para o servidor voltar sozinho quando a máquina religar.
- Suba com `docker-compose up -d` e acompanhe com `docker ps` e `docker-compose logs`.
- Prefira a conexão por SSM a depender de IP público: o ID da instância não muda.

Com o comando convertido em arquivo, o ambiente sobe com um comando, volta sozinho depois de uma parada e ligar outro serviço, como um banco, fica mais simples do que com `docker run`.
