Featured image of post Docker mais leve no WSL sem Docker Desktop

Docker mais leve no WSL sem Docker Desktop

Como executar Docker sem Docker Desktop

Introdução Link to this section

Docker Desktop oferece muitos recursos, mas também é pesado e lento para iniciar. Isso me fez removê-lo e instalar apenas o Docker Engine diretamente no WSL, reduzindo o tempo de inicialização de minutos para segundos.

Neste post mostrarei como fazer isso, algumas dicas de configuração, como compartilhar o socket do Docker entre distribuições WSL (como Rancher Desktop e Podman Desktop fazem automaticamente), e uma comparação com essas alternativas.

Aviso de performance do sistema de arquivos: montar código-fonte de /mnt/c (unidades do Windows) em containers é aproximadamente 10× mais lento do que usar o sistema de arquivos ext4 do WSL em ~/. O protocolo 9P cruzando a fronteira da VM é o gargalo. Mantenha seus repositórios em ~/ dentro do WSL e acesse-os do Windows via \\wsl.localhost\Ubuntu\home\seu-usuario\repo.

Terminologia Link to this section

  • Docker Engine: O componente central do Docker, responsável pela orquestração do runtime de containers, gerenciamento de imagens, rede e armazenamento.
  • Docker CLI: A interface de linha de comando para interagir com a API do Docker.

Por que fazer isso? Link to this section

As principais motivações para executar o Docker Engine diretamente no WSL em vez de Docker Desktop, Rancher Desktop ou Podman:

  • Custo de licença: Docker Desktop exige assinatura paga para empresas com 250+ funcionários ou receita anual acima de US$10M. Docker Engine é Apache 2.0 — gratuito para qualquer porte de empresa. Rancher Desktop e Podman Desktop também são gratuitos, mas têm compensações arquiteturais (veja comparação).
  • Tempo de inicialização: Docker Desktop pode levar minutos para iniciar. Docker Engine no WSL inicia em ~14 segundos com systemd, ou quase instantaneamente com um script de perfil.
  • Uso de recursos: sem GUI, sem camada extra de VM, sem Kubernetes embutido. Apenas dockerd rodando como serviço systemd (~100MB RAM, ~1-3% CPU ocioso). O k3s do Rancher Desktop sozinho usa ~500MB ocioso.
  • Controle total: você gerencia o Docker como um servidor Linux — systemd, daemon.json, arquivos de configuração. Sem camada de abstração GUI escondendo o que está acontecendo.
  • Compatibilidade nativa com Docker: socket, Compose, Swarm, BuildKit 100% compatíveis. Sem camada de emulação, sem proxy SSH, sem casos extremos de podman-compose.
  • Familiaridade: se seus servidores de produção rodam Docker Engine no Linux, seu ambiente de desenvolvimento é idêntico.

Quando esta abordagem é melhor Link to this section

  • Você é usuário de CLI e prefere terminal a GUIs.
  • Sua empresa precisa evitar custos de licenciamento do Docker Desktop.
  • Você usa Docker Compose extensivamente, incluindo network_mode: host ou mapeamentos de porta abaixo de 1024.
  • Você depende do Docker Swarm.
  • Você quer sobrecarga mínima de recursos na sua máquina de desenvolvimento.
  • Você já trabalha dentro do WSL para desenvolvimento e quer o daemon de containers na mesma distribuição.

Quando escolher outra alternativa Link to this section

Veja a comparação completa mais adiante neste post, mas resumindo:

  • Rancher Desktop se você precisa de GUI e Kubernetes com um clique.
  • Podman Desktop se sua política de segurança exigir containers rootless.
  • Docker Desktop se você precisa de containers nativos do Windows ou já possui licença.

Instalando o Docker Engine no WSL Link to this section

Para instalar o Docker Engine no WSL, primeiro siga a documentação oficial para sua distribuição. Eu uso Ubuntu, então darei instruções para ele neste post. Para outras distribuições, clique aqui.

Precisamos adicionar o repositório docker às fontes do apt e instalar seus componentes (Docker Engine, Docker CLI, Containerd, BuildX e Docker Compose):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Adicionar a chave GPG oficial do Docker:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Adicionar o repositório às fontes do Apt:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Para usar o comando docker sem permissões root, crie um grupo docker e adicione seu usuário a ele:

1
2
3
sudo groupadd -f docker
sudo usermod -aG docker $USER
newgrp docker

O comando newgrp docker inicia um novo shell com a associação ao grupo aplicada. Faça logout e login novamente para efeito permanente.

Em seguida, inicie o serviço Docker:

1
sudo service docker start

Se você habilitou o systemd no WSL (veja abaixo), use sudo systemctl start docker.

E teste com a imagem hello-world:

docker run hello-world

Como iniciar o Docker automaticamente ao iniciar o WSL Link to this section

Existem duas abordagens:

Com systemd (recomendado) Link to this section

WSL 0.67.6+ suporta systemd — o Docker inicia automaticamente como um serviço systemd.

Na sua distribuição WSL, edite /etc/wsl.conf (crie se não existir) e adicione:

1
2
[boot]
systemd=true

Então, do Windows (PowerShell ou CMD), desligue o WSL:

1
wsl --shutdown

Reabra seu shell WSL e execute docker ps para confirmar que o Docker está rodando.

Nota: systemd adiciona um pequeno atraso na inicialização (~14 segundos na minha máquina). Se isso for uma preocupação, use a abordagem do script de perfil abaixo.

Com script de perfil (alternativa, sem systemd) Link to this section

Se o systemd não estiver disponível ou você quiser evitar sua latência de inicialização, adicione o seguinte script ao seu perfil de shell (.zshrc, .bashrc, etc.):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if grep -qi "microsoft" /proc/version > /dev/null 2>&1; then
    if service docker status 2>&1 | grep -q "is not running"; then
        # Usa wsl.exe para executar como root sem solicitar senha.
        # Usa sudo como fallback se wsl.exe não estiver disponível.
        if command -v wsl.exe &> /dev/null; then
            wsl.exe --distribution "${WSL_DISTRO_NAME}" --user root \
                --exec /usr/sbin/service docker start > /dev/null 2>&1
        else
            sudo service docker start
        fi
    fi
fi

Isso verifica se você está dentro do WSL (caso compartilhe o perfil entre máquinas) e inicia o Docker se não estiver rodando.

Fonte: https://github.com/nickjj/dotfiles/commit/badd3265e5c8f6eca90d3b57df29292545332500

Como acessar o Docker do Windows Link to this section

Para acessar o socket Docker de fora do WSL, configure o daemon do Docker para escutar em uma porta TCP.

Se estiver usando systemd Link to this section

NÃO edite /lib/systemd/system/docker.service diretamente — as alterações são perdidas ao atualizar o pacote. Em vez disso, crie um override do systemd:

1
sudo systemctl edit docker.service

Adicione o seguinte, que limpa o ExecStart padrão e adiciona um listener TCP:

1
2
3
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

Então recarregue o systemd e reinicie o Docker:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

Nota de segurança: tcp://0.0.0.0:2375 expõe a API do Docker sem autenticação em todas as interfaces. O WSL2 usa uma rede NAT, o que limita a exposição, mas considere usar um firewall ou TLS se estiver em uma rede não confiável.

Se estiver iniciando com script de perfil (sem systemd) Link to this section

Edite /etc/docker/daemon.json e adicione a configuração hosts:

1
2
3
{
  "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
}

Então reinicie o Docker:

1
sudo service docker restart

Quando você define hosts no daemon.json, o socket Unix padrão não é usado a menos que você o inclua explicitamente (como mostrado acima).

Após qualquer uma das configurações, configure a variável de ambiente DOCKER_HOST no Windows:

1
DOCKER_HOST=tcp://localhost:2375

(ou tcp://[::1]:2375 para IPv6).

Com DOCKER_HOST definido, ferramentas como Testcontainers e Localstack usarão o Docker Engine rodando dentro do WSL.

💡
Escrevi posts sobre Testcontainers e Localstack.

Acesso pela linha de comando Link to this section

Prefiro executar comandos do shell WSL, mas existem alternativas:

  • Instalar o Docker CLI no Windows (ele respeita DOCKER_HOST).
  • Executar comandos via wsl: wsl -d Ubuntu docker ps

Comparação com alternativas Link to this section

Esta seção compara o Docker Engine no WSL com Rancher Desktop e Podman Desktop — as duas principais alternativas gratuitas que também evitam o custo de licença do Docker Desktop.

Rancher Desktop Link to this section

Docker Engine no WSLRancher Desktop
Apache 2.0, gratuito para todos os portesApache 2.0, gratuito para todos os portes
Sem GUI — apenas CLIDashboard GUI para containers, imagens, volumes
Sem Kubernetes embutido (DIY com kind/k3d/minikube)k3s embutido com seletor de versão
Menor uso de recursos (~14s inicialização com systemd, ~100MB daemon)Maior uso de recursos (k3s ocioso ~500MB RAM, ~3.8GB disco base)
Configuração manual (systemd, daemon.json, perfis)Configuração via GUI, camada de abstração sobre dockerd/nerdctl
Socket Docker nativo, Compose, Swarm — 100% compatívelCompatível com Docker via dockerd ou nerdctl

Rancher Desktop é a substituição mais próxima do Docker Desktop. É a escolha certa se você quer uma GUI e Kubernetes com um clique sem pagar pelo Docker Desktop. A compensação é maior uso de recursos e menos controle sobre o daemon — você gerencia o Docker através da GUI do Rancher em vez de diretamente.

Podman Desktop Link to this section

Docker Engine no WSLPodman Desktop
Baseado em daemon (dockerd roda como root)Sem daemon, rootless por padrão — containers rodam como seu usuário
Docker Compose 100% compatívelpodman-compose tem limitações: network_mode: host se comporta diferente em modo rootless, tradução de caminhos de bind-mount pode quebrar no WSL (resolve para caminhos D:\ em vez de caminhos WSL)
Docker Swarm suportadoDocker Swarm não suportado
BuildKit nativo — builds quentes ~6sBaseado em Buildah — builds quentes ~8s, diferenças de cache de camadas
Portas < 1024 (-p 80:80) funcionam normalmentePortas < 1024 exigem capacidades extras em modo rootless
Daemon sempre rodando (CPU background ~1-3%)Sem daemon persistente — zero uso de recursos quando ocioso

A arquitetura sem daemon e rootless do Podman é uma melhoria genuína de segurança — uma fuga de container dá ao atacante permissões de seu usuário, não de root. No entanto, o atrito de compatibilidade com ferramentas Docker é real. A maioria dos fluxos funciona com alias docker=podman, mas casos extremos aparecem no Docker Compose, mapeamento de portas e ferramentas que usam caminho fixo /var/run/docker.sock.

Escolha Podman se requisitos de segurança exigirem containers rootless ou seu time estiver alinhado com ferramentas Red Hat / OpenShift. Escolha Docker Engine no WSL se você quiser que tudo “simplesmente funcione” sem workarounds de configuração.

Possíveis erros Link to this section

Configuração inválida de credsStore Link to this section

Se o Docker Desktop foi instalado anteriormente, o armazenamento de credenciais pode estar definido como docker-credential-desktop, causando este erro:

1
error getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH, out: ``

Correção: edite ~/.docker/config.json e remova a propriedade credsStore.

Configuração duplicada de hosts Link to this section

Se você configurou o Docker com systemd e receber o seguinte erro ao iniciar, provavelmente é devido à configuração duplicada de hosts:

1
2
3
4
5
6
7
8
> sudo service docker restart
Job for docker.service failed because the control process exited with error code.
See "systemctl status docker.service" and "journalctl -xeu docker.service" for details.

> systemctl status docker.service
Aug 29 06:18:04 DanielPC systemd[1]: docker.service: Main process exited, code=exited, status=1/FAILURE
Aug 29 06:18:04 DanielPC systemd[1]: docker.service: Failed with result 'exit-code'.
Aug 29 06:18:04 DanielPC systemd[1]: Failed to start Docker Application Container Engine.

O arquivo de serviço do systemd passa hosts via flags -H, mas você também definiu hosts no /etc/docker/daemon.json. O Docker rejeita a configuração duplicada.

Para corrigir: remova a chave hosts do /etc/docker/daemon.json e configure o listener TCP via override do systemd como mostrado acima.

O que é 9P e por que isso importa? Link to this section

9P (Plan 9 Filesystem Protocol) é um protocolo de sistema de arquivos de rede dos Bell Labs. O WSL2 o usa para compartilhar unidades do Windows (NTFS) com a VM Linux. Quando você acessa C:\ de dentro do WSL em /mnt/c, toda operação de arquivo viaja pelo 9P:

1
Windows (NTFS)  ←→  Protocolo 9P sobre socket VM  ←→  VM Linux WSL2 (/mnt/c)

Cada stat(), readdir(), open(), close() serializa e faz ida-e-volta pela fronteira da VM. Para cargas de trabalho que acessam milhares de arquivos pequenos — npm install, git status, find, observadores de arquivo do Vite/webpack — isso se acumula em uma desaceleração de ~10× comparado ao ext4 nativo dentro do WSL.

Dentro de ~/ (o diretório home do WSL), os arquivos vivem em um sistema de arquivos ext4 em um arquivo VHDX — mesmo kernel, mesmo driver, sem overhead de tradução de protocolo. A diferença não é sobre Docker ou versão do WSL; é sobre se o caminho do sistema de arquivos cruza a fronteira do 9P.

A correção é sempre a mesma independente de qual ferramenta de container você usa: armazene código-fonte em ~/ dentro do WSL (não em /mnt/c/), faça bind-mount de caminhos Linux para dentro dos containers, e acesse arquivos de ferramentas Windows via o caminho de rede \\wsl.localhost\Ubuntu\home\seu-usuario\repo.

E o compartilhamento de rede \\wsl.localhost\? Link to this section

O Windows expõe o sistema de arquivos de cada distribuição WSL como um compartilhamento SMB oculto em \\wsl.localhost\Ubuntu\, \\wsl.localhost\Debian\, etc. Você pode até mapeá-lo para uma letra de unidade:

1
net use Z: \\wsl.localhost\Ubuntu

Isso é Windows acessando arquivos Linux pela rede — passa pela pilha de rede do Windows, não pelo 9P. Esta é a forma recomendada de editar arquivos WSL de aplicações Windows (VS Code, Explorer, etc.) sem passar pelo caminho lento do 9P.

Crucialmente: isso NÃO afeta a performance de bind-mount do Docker. O Docker dentro do WSL monta caminhos Linux usando o kernel Linux. Se seu repositório está em ~/meu-projeto, você monta como -v ~/meu-projeto:/app — ext4 Linux para overlayfs Linux, sem envolvimento do 9P. O caminho SMB do Windows é apenas para edição, não para montagens de container.

Posts de blog mais antigos podem recomendar o oposto — armazenar código no Windows e acessá-lo via /mnt/c — mas esse padrão é o caminho lento. Mantenha o código no WSL, edite do Windows via o compartilhamento \\wsl.localhost\.

Referências Link to this section

💬 Like or have something to add? Leave a comment below.
Ko-fi
GitHub Sponsor
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy