ComfyUI Headless: Rode Como um Serviço, Use de Qualquer Lugar
- Categoria
- IA e LLM Local
- Publicado em
- 11 julho 2026
- Atualizado em
- 11 julho 2026
- Por
- Jacob Lloyd — escrito com ajuda de IA, depois do projeto
- Tempo de leitura
- 10 min de leitura
Em termos simples: Meu computador de geração de imagens roda o ComfyUI o tempo todo em segundo plano, sem monitor e sem ninguém logado em um desktop. Consigo iniciá-lo, checar o status, ou pedir uma imagem com um comando de uma linha só, e consigo abrir a interface completa dele pelo notebook ou celular através de uma conexão privada e criptografada. Um pequeno plugin que criei garante que todo workflow que eu salvo de qualquer dispositivo pousa naquele mesmo computador, para nada ficar espalhado.
No último artigo, deixei o ComfyUI + Z-Image Turbo gerando uma imagem de 1024px em ~27 segundos em um mini PC com AMD. Esta é a continuação: transformar essa instalação em um eletrodoméstico sempre ligado — sem sessão de desktop, sem ficar de babá do terminal — que qualquer dispositivo da casa consegue usar, enquanto tudo o que ele produz e todo workflow que salva fica naquela mesma máquina.
tl;dr
- O que é: ComfyUI rodando em modo headless sob um serviço systemd de usuário, controlado por uma CLI pequena, acionado via script pela sua API HTTP, e acessado de outros dispositivos através de uma VPN mesh com HTTPS.
- Quanto custa: de graça. O nível de VPN que faz isso (Tailscale pessoal) também é gratuito.
- O que você precisa: uma instalação funcional do ComfyUI e mais ou menos uma noite.
- O que você ganha:
comfyctl generate "..."a partir de qualquer shell, a interface completa no notebook ou celular, e uma biblioteca de workflows que mora em uma única pasta no servidor, não importa de qual dispositivo você salvou.
O que você ganha
No dia a dia, "usar a máquina de imagens" se resume a uma de três coisas, e nenhuma delas envolve sentar na frente dela:
comfyctl status # server health + systemd state + newest output file
comfyctl generate "a foggy harbor at dawn, cinematic"
comfyctl logs 100
...ou um script em outra máquina fazendo POST de um prompt para a API HTTP, ou a interface completa de grafo de nós aberta em uma aba do navegador no meu notebook do outro lado da casa — via HTTPS, através da VPN, com os salvamentos pousando no disco do servidor.
Por que headless
A interface web é como você projeta um workflow. É um jeito péssimo de rodar o mesmo workflow pela 200ª vez. Depois que um workflow está calibrado, o que você realmente quer é um eletrodoméstico: uma máquina que está sempre ligada, sempre pronta, e responde a pedidos de qualquer coisa que peça — uma linha de comando no shell, uma tarefa cron, um chatbot que precisa de um avatar novo às 3 da manhã.
Headless aqui não significa "nunca ter interface". O servidor não tem sessão de desktop nem navegador próprio; a interface continua existindo, servida via HTTP para qualquer dispositivo que a queira. O servidor faz o trabalho, tudo o mais é só uma tela.
O serviço systemd de usuário
O padrão de serviço é o mesmo do artigo de montagem, então vou manter isso curto. Uma unidade systemd de usuário em ~/.config/systemd/user/comfyui.service:
[Unit]
Description=ComfyUI server
After=network-online.target
[Service]
ExecStart=%h/comfy/start-comfyui.sh
WorkingDirectory=%h/comfy/ComfyUI
EnvironmentFile=-%h/comfy/comfy.env
Restart=on-failure
RestartSec=5
TimeoutStartSec=120
[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now comfyui.service
loginctl enable-linger $USER # mantém os serviços de usuário rodando mesmo sem ninguém logado
Essa última linha é a parte específica de headless que as pessoas costumam esquecer: sem o linger, as unidades de usuário param quando sua última sessão termina — o que, em uma máquina headless, significa "imediatamente". O EnvironmentFile mantém a porta e as flags de inicialização em um único lugar editável (comfy.env), para que outras ferramentas leiam o mesmo valor em vez de fixar 8188 no código.
Um script de controle no estilo comfyctl
O systemctl puro funciona, mas um wrapper fino faz a máquina parecer um produto. O meu tem ~100 linhas de bash e se chama comfyctl:
comfyctl start # systemctl start + faz polling em /system_stats até ficar saudável
comfyctl stop
comfyctl restart
comfyctl status # saúde + estado da unidade + PNG de saída mais recente
comfyctl generate "<prompt>" [--seed N --steps 8 --width 1024 --out PATH]
comfyctl logs [N] # journalctl --user -u comfyui.service -n N
Os detalhes que fazem isso ser agradável, e não só curto:
- Saúde significa que a API responde, não que o processo existe.
curl -sf http://127.0.0.1:$PORT/system_statsem loop, com um orçamento de 60 segundos. O systemd dizer "active" só significa que o Python iniciou; um servidor de modelo não está "de pé" até responder HTTP. - Ele lê a porta do mesmo arquivo de ambiente que o serviço usa, então mudar a porta em um lugar só não deixa a ferramenta perdida.
generateinicia o servidor automaticamente. Se a checagem de saúde falhar, ele inicia a unidade, espera ficar saudável, e só então envia. Quem chama nunca precisa saber se a máquina estava acordada.statusimprime o arquivo mais novo emoutput/— a resposta para "minha última geração realmente funcionou" sem abrir nada.
Controlando a API HTTP a partir de scripts
Tudo o que a interface faz passa pela mesma API HTTP, e o núcleo do loop são três endpoints. Um workflow é só JSON (exportado via Save (API format) na interface); um script o carrega, troca o prompt e a seed, e:
import json, time, urllib.request
SERVER = "http://127.0.0.1:8188"
graph = json.load(open("workflow_api.json"))
graph["6"]["inputs"]["text"] = "a foggy harbor at dawn, cinematic" # id do seu nó de prompt
# 1. enfileira
req = urllib.request.Request(f"{SERVER}/prompt",
data=json.dumps({"prompt": graph}).encode(),
headers={"Content-Type": "application/json"})
pid = json.load(urllib.request.urlopen(req))["prompt_id"]
# 2. faz polling do histórico até terminar
while True:
hist = json.load(urllib.request.urlopen(f"{SERVER}/history/{pid}"))
if pid in hist: break
time.sleep(1)
# 3. busca a imagem
img = hist[pid]["outputs"]["9"]["images"][0] # id do seu nó SaveImage
url = f"{SERVER}/view?filename={img['filename']}&subfolder={img['subfolder']}&type={img['type']}"
open("result.png", "wb").write(urllib.request.urlopen(url).read())
Só biblioteca padrão — sem pacote cliente do ComfyUI, sem chave de API. Esse é o padrão por trás do comfyctl generate, e é a peça que transforma a máquina em infraestrutura: qualquer coisa capaz de fazer uma requisição HTTP agora consegue gerar imagens.
O plugin Workbench: sua biblioteca de workflows mora na máquina
Aqui está o problema que apareceu na primeira semana de uso remoto. O "Export" padrão do ComfyUI salva o JSON do workflow através do mecanismo de download do navegador — no dispositivo de onde você estiver navegando. Use a interface de três dispositivos e sua biblioteca de workflows fica espalhada em três pastas de Downloads, nenhuma delas a máquina que de fato roda os workflows.
Então construí um pequeno custom node ("Workbench") que corrige a direção da gravidade. É um plugin custom_nodes normal: o lado Python registra algumas rotas no próprio servidor web do ComfyUI, o lado JS adiciona uma aba na barra lateral. Ele faz dois trabalhos:
1. Salvar/abrir na pasta de trabalho
O botão Save da barra lateral envia (POST) o grafo atual para o plugin, que o grava na pasta canônica do ComfyUI user/default/workflows no servidor — nome de arquivo sanitizado, confinado àquela pasta, gravação atômica (arquivo temporário + rename), e um .bak.json rotativo de tudo que é sobrescrito. Open lista essa mesma pasta, do mais novo para o mais antigo.
O ponto-chave: a interface roda em um navegador remoto, mas o salvamento vai para o disco local do servidor. Salve do notebook, abra do celular, rode a partir de um script — uma biblioteca só, uma pasta só, na máquina com a GPU. (Na minha máquina, essa pasta está com um link simbólico para um diretório simples ~/comfy/workflows, então também é trivial de fazer backup.) Como o frontend só faz chamadas relativas à origem do servidor de onde foi carregado, ele se comporta de forma idêntica em localhost e através de um proxy — sem configuração por dispositivo.
2. Varredura de modelos faltando + downloads na lista de permissões
A outra dor do uso remoto: você abre um workflow e ele quer um arquivo de modelo que a máquina não tem. Baixar 12GB no celular e reenviar é obviamente errado. O plugin compara o workflow carregado com cada pasta de modelo registrada, lista o que está faltando, e baixa do lado do servidor, direto na pasta correta models/<pasta> — transmitido para um arquivo .part, retomável via HTTP Range, verificação sha256 opcional, com o progresso enviado para a interface via websocket.
Como "o navegador pode mandar o servidor baixar URLs arbitrárias para o disco" é uma frase assustadora, isso está bem cercado:
- Só HTTPS, e o hostname precisa bater com uma lista de permissões em
config.json(por exemplo,huggingface.co,civitai.com, curingas como*.hf.co) — e cada salto de redirecionamento é revalidado contra a mesma lista, já que redirecionar para qualquer lugar é o clássico jeito de burlar listas de permissões. - Nomes de arquivo são reduzidos a um nome-base sanitizado e precisam terminar em uma extensão permitida (
.safetensorsetc.); o destino precisa ser uma pasta de modelo registrada — nada de path traversal. - Arquivos existentes nunca são sobrescritos, existe um limite de tamanho por download (30GB por padrão), e os downloads ficam em fila, um de cada vez.
Acesso remoto seguro: VPN mesh + HTTPS
Nesta máquina, o ComfyUI escuta só em 127.0.0.1 — fixo no código, não uma flag. Ele não tem página de login, então nunca pode ser exposto à internet, e honestamente nem à rede local crua. A resposta limpa é uma VPN mesh como o Tailscale: todo dispositivo ganha um endereço privado criptografado, nada é acessível de fora da sua conta, e o recurso serve dele age como um proxy reverso HTTPS local:
tailscale serve --bg --https=8443 http://127.0.0.1:8188
Agora https://<sua-máquina>.<sua-tailnet>.ts.net:8443 serve a interface completa, com um certificado TLS real (provisionado automaticamente), só para os seus dispositivos. O próprio ComfyUI continua só ouvindo o localhost — o proxy é a única porta de entrada.
HTTPS não é uma gentileza opcional aqui. Os navegadores restringem cada vez mais coisas (área de transferência, alguns comportamentos de worker/websocket) em HTTP puro de origens que não são localhost, e você está mandando seus prompts e imagens pela rede. A VPN mesh te dá um certificado real sem você precisar de um domínio ou abrir uma porta. Uma nota prática: conecte pelo nome DNS da máquina, não pelo IP puro da VPN — o certificado TLS é emitido para o nome, então o IP vai falhar na checagem do certificado.
Pegadinhas
- 403 que só acontece remotamente é checagem de origem, não autenticação. O middleware do servidor do ComfyUI valida
Origin/Hoste afins em requisições que mudam de estado; algumas configurações de proxy encaminham cabeçalhos que não batem com o esperado (peguei exatamente isso ao conectar outra ferramenta — um 403 baseado emSec-Fetch-Site). Corrija os cabeçalhos encaminhados pelo proxy, ou restrinja as configurações de CORS do ComfyUI (--enable-cors-header) à sua única origem confiável — nunca*. - Use o hostname da VPN, não o IP. O HTTPS via VPN mesh é certificado-por-nome; acessar o endereço cru dá erros de TLS que parecem que o servidor está quebrado.
- Frontends de custom node precisam ser relativos à origem. Qualquer
http://127.0.0.1:8188/...fixo no JS de um plugin funciona na máquina e quebra silenciosamente através do proxy. Use oapi.fetchApi()do ComfyUI e caminhos relativos. - Websockets através de um proxy podem cair. Os eventos de progresso viajam pelo websocket; atrás de um proxy, ele às vezes morre silenciosamente. O Workbench faz polling como alternativa exatamente por isso — projete pensando nisso.
loginctl enable-linger, ou seu "serviço" morre ao deslogar.- "Unidade ativa" ≠ "servidor de pé".
- Mantenha a lista de permissões de download restrita.
- Nunca faça port-forward disso para a internet. Sem autenticação + execução arbitrária de workflow + (com um downloader) gravação em disco. VPN mesh ou nada.
Enquanto o primeiro artigo terminou com um computador que consegue gerar imagens, este termina com algo melhor: uma máquina quieta em uma prateleira que qualquer dispositivo e script que eu tenho consegue usar — e que mantém tudo o que faz em um só lugar.