Kernel do LineageOS para Samsung Galaxy S20 / S20+ / S20 Ultra e Note 20 (Exynos 990) recompilado com as opções necessárias para rodar Docker.
O kernel que o LineageOS distribui vem com namespaces de IPC, filas POSIX,
cgroup de devices e a ponte de rede desativados, o que impede o dockerd de
subir. Este repositório contém a receita completa para recompilar habilitando
essas opções, além do ferramental para gravar e verificar o resultado.
Status: validado em aparelho real. SM-G985F (y2s), LineageOS 23.2,
Android 16, kernel 4.19.325.
- O problema
- O detalhe que faz funcionar
- Requisitos
- Passo a passo
- Gravar no aparelho
- Recuperação
- Armadilhas do Kconfig
- Limitações conhecidas
- Custo recorrente
- Estrutura do repositório
Auditando o kernel de fábrica do LineageOS contra os requisitos do Docker, faltam três itens obrigatórios e três de rede:
| Opção | Sem ela |
|---|---|
CONFIG_IPC_NS |
o runc não cria o namespace de IPC de cada container |
CONFIG_POSIX_MQUEUE |
o runc monta /dev/mqueue em todo container; falha |
CONFIG_CGROUP_DEVICE |
sem controle de acesso a devices |
CONFIG_BRIDGE |
sem docker0 |
CONFIG_BRIDGE_NETFILTER |
sem firewall na ponte |
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE |
sem as regras de porta do Docker |
Não há flag de linha de comando que contorne a ausência de IPC_NS ou
POSIX_MQUEUE: é recompilação ou nada.
Já vêm habilitadas de fábrica, felizmente: OVERLAY_FS, VETH, NF_NAT,
SECCOMP, MEMCG, CGROUP_BPF, BLK_CGROUP, CFS_BANDWIDTH,
NF_CONNTRACK. O script flash/docker-check.sh audita seu aparelho e diz
exatamente o que falta.
A invocação do make precisa ser:
make O=out LLVM=1 LLVM_IAS=1 ARCH=arm64 READELF=$CLANG/bin/llvm-readelf -j$(nproc)LLVM=1 ativa toda a suíte LLVM. LLVM_IAS=1 usa o assembler integrado do
clang no lugar do GNU as — e é esse o ponto decisivo.
Kernels compilados com o as do binutils (o que a documentação do LineageOS
sugere, com apenas CC=clang LD=ld.lld) carregam mas não executam neste
bootloader. O log do bootloader mostra Starting kernel... e nada depois:
nenhuma mensagem, nenhum pânico, nenhum registro em pstore. O kernel morre
antes de inicializar o console.
Quatro builds foram descartadas até isolar isso. O histórico completo da
investigação, com todos os testes intermediários, está em
research/.
- Aparelho Exynos 990 com bootloader destravado e Magisk instalado
- Ambiente Linux com ~40 GB livres (WSL2 no Windows serve)
- adb (platform-tools) no computador
- 12 GB de RAM recomendados para a build com LTO
Codinomes suportados pela árvore: x1s, x1slte (S20), y2s, y2slte
(S20+), z3s (S20 Ultra), c1s, c1slte (Note 20), c2s, c2slte
(Note 20 Ultra), r8s (S20 FE).
adb shell getprop ro.product.deviceSe não for y2s, edite build/04-build.sh e troque y2s.config pelo
fragmento correspondente.
adb push flash/docker-check.sh /data/local/tmp/
adb shell su -c 'sh /data/local/tmp/docker-check.sh'O script lê /proc/config.gz e lista, opção por opção, o que falta. Se o
veredito já for verde, você não precisa deste repositório.
sudo bash build/01-deps.shInstala clang, bison, flex, libssl-dev, device-tree-compiler e o
restante do necessário. Detecta se já está como root e dispensa o sudo.
bash build/02-fontes.shBaixa ~3,2 GB para ~/kernel/:
LineageOS/android_kernel_samsung_universal9830, branchlineage-23.2clang-r416183b, o toolchain que oBoardConfigCommon.mkdo device tree declara
E instala config/docker-kernel.config em arch/arm64/configs/.
Confira a branch. Se sua ROM não for LineageOS 23.2, ajuste
-bno script. Kernel de branch errada não boota.
bash build/03-gcc.shO clang não traz as/ld/objcopy. Este script clona o prebuilt
aarch64-linux-android-4.9 do AOSP (~91 MB).
bash build/04-build.shAntes de compilar, o script valida três coisas e aborta se alguma falhar:
CONFIG_LTO_CLANG=ysobreviveu aoolddefconfig- as opções do Docker entraram no
.configfinal - a
Imagegerada tem tamanho compatível com o kernel original
Leva de 6 a 10 minutos. Resultado em out/Image-ias.
Não se grava a Image diretamente: ela precisa ser inserida dentro de uma
boot.img, que também contém o ramdisk e o device tree. O
flash/kernel-swap.sh faz isso usando o magiskboot que já está no aparelho.
Ele parte de um backup da sua boot atual, que já está patchada com o Magisk. Como o patch do Magisk vive no ramdisk, e a troca substitui só o kernel, o root sobrevive — não é preciso repassar pelo app do Magisk.
# 1. envia o kernel novo
adb push out/Image-ias /data/local/tmp/Image
# 2. envia as ferramentas
adb push flash/kernel-swap.sh flash/verify-patched.sh /sdcard/Download/
adb shell su -c 'cp /sdcard/Download/*.sh /data/local/tmp/ && chmod 755 /data/local/tmp/*.sh'
# 3. monta a boot.img nova (NAO grava nada ainda)
adb shell su -c 'sh /data/local/tmp/kernel-swap.sh'O kernel-swap.sh faz backup da boot atual em dois lugares
(/data/local/tmp/ e /sdcard/Download/), mostra o md5 do kernel antigo e do
novo, e gera boot-novo.img. Se o backup já existir, ele não sobrescreve —
assim uma segunda execução não substitui o original por um já modificado.
# 4. PUXE O BACKUP PARA O COMPUTADOR - passo que salva a pele
adb pull /sdcard/Download/boot-original.img .
# 5. grava
adb shell su -c 'dd if=/data/local/tmp/boot-novo.img of=/dev/block/by-name/boot bs=4096'
adb shell su -c sync
adb rebootO passo 4 não é opcional. Se o aparelho não bootar,
/datafica inacessível — o recovery não consegue montar uma partição f2fs com checkpoint sujo. Um backup que só existe dentro do aparelho não serve para nada nesse momento.
Se preferir passar pelo app do Magisk (Instalar → Selecionar e corrigir um
arquivo → boot-novo.img), o flash/verify-patched.sh confere, antes de
gravar, se o kernel dentro da imagem é o que você compilou e se o Magisk foi
mesmo aplicado.
Um kernel que não boota não danifica nada. A partição /data nunca é
tocada: seus dados, aplicativos e arquivos continuam intactos. Só a partição de
boot, de 59 MB, precisa ser regravada.
- Power + Volume Baixo por ~10 s, até a tela apagar
- Com o cabo USB conectado, Volume Alto + Power até o recovery abrir
- No recovery: Advanced → Enable ADB
adb push boot-original.img /tmp/boot.img
adb shell dd if=/tmp/boot.img of=/dev/block/by-name/boot bs=4096
adb rebootO LineageOS publica a boot.img avulsa de cada build — 59 MB, não é preciso
baixar a ROM inteira:
curl -s https://download.lineageos.org/api/v2/devices/y2s/builds | grep -o 'https://[^"]*boot\.img'Confira o sha256 contra o que a API informa antes de gravar. Essa imagem é limpa, sem Magisk: você recupera o aparelho, mas precisa refazer o root.
Depois de um boot fracassado, o log do bootloader fica em /proc/last_kmsg,
acessível pelo recovery:
adb pull /proc/last_kmsgProcure o fim do arquivo. Se terminar em Starting kernel... sem nenhuma linha
do kernel, a imagem não executou — provável problema de build, não de
configuração.
Três comportamentos silenciosos custaram tempo nesta investigação. Todos os
scripts deste repositório verificam o .config final, nunca o que foi
pedido.
O merge_config.sh trata # CONFIG_X is not set como uma definição — é a
forma que o Kconfig usa para "desativado". Uma linha de documentação assim:
# CONFIG_OVERLAY_FS=y ja vem habilitada no defconfig
é interpretada como ordem de desligar OVERLAY_FS, e remove silenciosamente o
=y que vinha do defconfig base.
Regra: em fragmentos de configuração, nunca escreva CONFIG_ logo após o
#. Coloque outra palavra antes.
config LTO_CLANG
depends on ARCH_SUPPORTS_LTO_CLANG
depends on CC_IS_CLANG && LD_IS_LLD
Compilando com GNU ld, a dependência LD_IS_LLD não é satisfeita, o Kconfig
resolve a escolha para LTO_NONE sem emitir aviso, e o kernel sai 3,4 MB
maior. LLVM=1 resolve.
Qualquer opção cuja dependência não esteja satisfeita é revertida em silêncio.
O build/04-build.sh confere uma a uma depois do olddefconfig e aborta se
alguma não entrou — descobrir isso depois de compilar e gravar custa muito mais
caro.
O dockerd precisa rodar com iptables: false. O motivo:
O chroot compartilha o namespace de rede do Android, cujas tabelas contêm
regras com o match quota2 (contabilidade de dados móveis da Samsung). O
iptables do Debian consegue ler essas regras depois de instalar
xtables-addons-common, mas ao gravar precisa re-serializar a tabela
inteira — e o layout do quota2 do xtables-addons difere do xt_quota2 da
Samsung. O kernel rejeita a escrita com No chain/target/match by that name.
Consequência: use --network=host. O container abre a porta direto no
aparelho, o que para um servidor caseiro costuma bastar — só não repita portas
entre containers.
A solução definitiva seria dar ao chroot um namespace de rede próprio, com par
veth e NAT feito pelo iptables do Android (que entende quota2).
O namespace de usuário é vetor conhecido de escalada de privilégio e vem
desabilitado nos kernels Android por decisão de segurança. O Docker comum não
precisa dele — só o modo rootless. Se quiser, descomente em
config/docker-kernel.config.
O dockerd avisa No cpu shares support e No cpuset support: a delegação de
cgroup dentro do chroot é parcial. Limite de memória (--memory) funciona.
Toda atualização do LineageOS regrava a partição de boot, apagando o kernel customizado e o Magisk. Em builds nightly, isso é semanal.
Refazer leva ~10 minutos:
bash build/04-build.sh # ~7 min, a árvore já está clonada
# kernel-swap + dd + reboot # ~3 minMas exige você presente. Se o ritmo incomodar, considere um canal de atualização menos frequente.
build/ receita de compilação, na ordem de execução
01-deps.sh dependências do sistema
02-fontes.sh clona kernel + clang, instala o fragmento
03-gcc.sh binutils cruzado do AOSP
04-build.sh compila (LLVM=1 LLVM_IAS=1) com validação
config/ fragmentos de configuração do kernel
docker-kernel.config completo (rede + cgroups opcionais)
docker-minimal.config só o essencial, sem os cgroups opcionais
flash/ ferramentas para o aparelho
docker-check.sh audita o kernel em uso contra os requisitos
kernel-swap.sh troca o kernel dentro da boot.img
verify-patched.sh confere a imagem antes de gravar
flash-kernel.sh grava com leitura de volta
repack-test.sh verifica idempotência do magiskboot
research/ registro da investigação: 15 scripts de teste
Este repositório resolve só o kernel. Para o servidor Linux em si — imagem ext4, chroot com systemd, boot automático pelo Magisk, SSH — veja android-chroot-server, que inclui também a instalação e configuração do Docker dentro do chroot.
MIT. Veja LICENSE.
Os fragmentos de configuração são derivados do defconfig do kernel do LineageOS, que é GPLv2 — como qualquer código do kernel Linux.