Introdução
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
- 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?
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
- 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: hostou 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
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
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):
| |
Para usar o comando docker sem permissões root, crie um grupo docker e adicione seu usuário a ele:
| |
O comando
newgrp dockerinicia 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:
| |
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
Existem duas abordagens:
Com systemd (recomendado)
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:
| |
Então, do Windows (PowerShell ou CMD), desligue o WSL:
| |
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)
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.):
| |
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
Para acessar o socket Docker de fora do WSL, configure o daemon do Docker para escutar em uma porta TCP.
Se estiver usando systemd
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:
| |
Adicione o seguinte, que limpa o ExecStart padrão e adiciona um listener TCP:
| |
Então recarregue o systemd e reinicie o Docker:
| |
Nota de segurança:
tcp://0.0.0.0:2375expõ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)
Edite /etc/docker/daemon.json e adicione a configuração hosts:
| |
Então reinicie o Docker:
| |
Quando você define
hostsnodaemon.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:
| |
(ou tcp://[::1]:2375 para IPv6).
Com DOCKER_HOST definido, ferramentas como Testcontainers e Localstack usarão o Docker Engine rodando dentro do WSL.
Acesso pela linha de comando
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
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
| Docker Engine no WSL | Rancher Desktop |
|---|---|
| Apache 2.0, gratuito para todos os portes | Apache 2.0, gratuito para todos os portes |
| Sem GUI — apenas CLI | Dashboard 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ível | Compatí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
| Docker Engine no WSL | Podman Desktop |
|---|---|
Baseado em daemon (dockerd roda como root) | Sem daemon, rootless por padrão — containers rodam como seu usuário |
| Docker Compose 100% compatível | podman-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 suportado | Docker Swarm não suportado |
| BuildKit nativo — builds quentes ~6s | Baseado em Buildah — builds quentes ~8s, diferenças de cache de camadas |
Portas < 1024 (-p 80:80) funcionam normalmente | Portas < 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
Configuração inválida de credsStore
Se o Docker Desktop foi instalado anteriormente, o armazenamento de credenciais pode estar definido como docker-credential-desktop, causando este erro:
| |
Correção: edite ~/.docker/config.json e remova a propriedade credsStore.
Configuração duplicada de hosts
Se você configurou o Docker com systemd e receber o seguinte erro ao iniciar, provavelmente é devido à configuração duplicada de hosts:
| |
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?
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:
| |
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\?
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:
| |
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\.
