Como rodar o servidor llama.cpp em uma VPS
Compile o llama-server de uma tag fixa, sirva modelos GGUF pela API compatível com OpenAI, use localhost e limite a memória com systemd.
O que está a configurar
Executar o servidor llama.cpp numa VPS significa usar um único binário, llama-server, que carrega um único ficheiro de modelo GGUF e responde a pedidos HTTP através de uma API compatível com OpenAI. Aponte qualquer cliente OpenAI para http://127.0.0.1:8080/v1 e ele funcionará. A instalação é a parte mais simples.
O restante trabalho é operacional: fixar uma versão, manter a porta apenas em localhost, criar uma unidade systemd e decidir o que acontece quando o servidor fica sem memória. É disso que este guia trata. Se ainda não decidiu entre as duas opções mais óbvias, leia primeiro as diferenças entre Ollama e llama.cpp, porque este é o guia prático que essa comparação deixa de fora de propósito.
Escolha uma tag de release e registe-a
O llama.cpp cria uma tag de release para quase todos os merges, por isso as tags funcionam como números de build. b10488 é a mais recente em 18 de agosto de 2026. Não existe uma branch estável de longa duração. Isto significa que "latest" muda continuamente e que a versão testada é a única que pode suportar. Escolha uma tag, registe-a e use a mesma string no clone, no nome do binário e nas suas notas.
Cada tag também disponibiliza arquivos pré-compilados. Para uma VPS x86 apenas com CPU, o arquivo é llama-b10488-bin-ubuntu-x64.tar.gz. Existe um arquivo arm64 ao lado desse, caso esteja numa VPS ARM em vez de x86.
curl -LO https://github.com/ggml-org/llama.cpp/releases/download/b10488/llama-b10488-bin-ubuntu-x64.tar.gz
tar tf llama-b10488-bin-ubuntu-x64.tar.gz | headListe o arquivo antes de o extrair, para saber onde os ficheiros serão colocados. Esses binários estão ligados à biblioteca C da imagem que os compilou. Por isso, numa distribuição mais antiga, falham no arranque com um erro que indica uma versão de GLIBC_ não instalada. Compilar a partir do código-fonte demora alguns minutos numa VPS pequena e elimina toda essa classe de problemas. É esse o caminho usado abaixo.
Compile o llama-server a partir de uma tag fixada
sudo apt update
sudo apt install -y build-essential cmake git libssl-dev
git clone --depth 1 --branch b10488 https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2--branch b10488 num --depth 1 clone faz checkout dessa tag e de mais nada, para que o build não sofra alterações durante o trabalho.
libssl-dev é importante porque a opção LLAMA_OPENSSL fica ativada por predefinição. É ela que permite ao binário descarregar modelos por HTTPS mais tarde. Sem os cabeçalhos, o passo de configuração falha.
-DBUILD_SHARED_LIBS=OFF gera um único binário autónomo. O build predefinido coloca bibliotecas partilhadas junto do executável. Por isso, copiar apenas o executável para /usr/local/bin falha com error while loading shared libraries: libllama.so.
-t llama-server compila apenas o alvo do servidor. O build predefinido também compila as restantes ferramentas e os testes. Num VPS com dois núcleos, isso acrescenta vários minutos a compilar ficheiros que nunca irá executar.
-j 2 é intencional. Cada tarefa de compilação paralela mantém o seu próprio conjunto de trabalho. Por isso, -j $(nproc) num plano pequeno termina com c++: fatal error: Killed signal terminated program cc1plus, quando o killer de falta de memória do kernel interrompe o compilador. Reduza o número de tarefas ou adicione swap para o build.
Pode querer alterar uma opção: GGML_NATIVE fica ativada por predefinição, por isso o compilador direciona o binário para a CPU exata onde o build é executado. É isso que deve usar quando compila na máquina que irá executar o binário. Se compilar uma vez e copiar o binário para outro host, adicione -DGGML_NATIVE=OFF. Caso contrário, um binário que use instruções inexistentes na outra CPU termina com Illegal instruction (core dumped) na primeira inferência.
Instale-o com um nome que inclua a tag.
./build/bin/llama-server --version
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-b10488
sudo ln -sfn /usr/local/bin/llama-server-b10488 /usr/local/bin/llama-server--version apresenta o número do build e o commit. Estes valores têm de corresponder à tag que selecionou. Se não corresponderem, compilou outra versão. Manter o número no nome do ficheiro e apontar um symlink para esse ficheiro faz com que uma atualização exija um ln -sfn e um reinício. Para reverter, execute o mesmo comando com o número antigo.
Obtenha um modelo GGUF e verifique o disco primeiro
GGUF é o formato de ficheiro único carregado pelo llama.cpp. Um ficheiro contém os pesos, o tokenizador e os metadados, pelo que não é necessário instalar mais nada. O sufixo do nome do ficheiro indica a quantização, ou seja, a precisão com que os pesos são armazenados: Q4_K_M é uma combinação de 4 bits, Q8_0 usa 8 bits e f16 é o ficheiro de meia precisão não quantizado.
Crie uma conta de serviço e um diretório para o modelo antes de iniciar qualquer transferência.
sudo useradd --system --home /srv/llama --create-home --shell /usr/sbin/nologin llama
sudo install -d -o llama -g llama /srv/models
df -h /srvO servidor pode obter o modelo diretamente com -hf. Esta é a forma mais rápida de confirmar que a compilação funciona.
sudo -u llama env LLAMA_CACHE=/srv/models /usr/local/bin/llama-server \
-hf ggml-org/gemma-3-1b-it-GGUF:Q4_K_M --host 127.0.0.1 --port 8080LLAMA_CACHE define o diretório de transferência. Sem esta opção, o ficheiro é guardado em ~/.cache/llama.cpp, na conta que executou o comando. Esse é o local errado para um serviço cujo diretório pessoal ficará sem permissões de leitura. Execute ls -lh /srv/models depois, porque o nome do ficheiro em cache é derivado do nome do repositório, e não do nome simples do ficheiro.
Num serviço, transfira o modelo para um caminho escolhido por si, para que o ficheiro da unidade tenha um destino estável.
sudo -u llama curl -L --output-dir /srv/models -O \
https://huggingface.co/ggml-org/gemma-3-1b-it-GGUF/resolve/main/gemma-3-1b-it-Q4_K_M.ggufO espaço em disco é o primeiro limite encontrado pela maioria das pessoas. Estes são os tamanhos publicados de dois modelos, verificados em 18 August 2026.
The data behind this chart
[
{
"label": "gemma-3-1b-it Q4_K_M",
"size_gb": 0.81
},
{
"label": "gemma-3-1b-it Q8_0",
"size_gb": 1.07
},
{
"label": "gemma-3-1b-it f16",
"size_gb": 2.01
},
{
"label": "gpt-oss-20b MXFP4",
"size_gb": 12.11
}
]O ficheiro de 4 bits do modelo 1B tem 0.81 GB. O mesmo modelo sem quantização tem 2.01 GB, pelo que a escolha do formato altera o valor em mais do dobro. Um modelo 20B em MXFP4 tem 12.11 GB. Esse tamanho não cabe no disco de muitos planos de entrada e, depois, o modelo ainda tem de ser lido para a memória. Se estiver a considerar uma família específica, o mesmo cálculo de dimensionamento para GLM mostra a rapidez com que o preço do modelo principal deixa de ser viável numa VPS, enquanto uma variante menor cabe no disco.
Verifique df -h antes de cada transferência. Um sistema de ficheiros raiz que fique cheio durante uma transferência de 12 GB impede tudo o que precise de escrever, incluindo o journal.
Execute-o uma vez manualmente e verifique o resultado
sudo -u llama /usr/local/bin/llama-server \
--model /srv/models/gemma-3-1b-it-Q4_K_M.gguf \
--host 127.0.0.1 --port 8080 \
--ctx-size 4096 --parallel 1 --threads 2 --no-webuiNuma segunda sessão, pergunte ao servidor se está pronto.
curl -s http://127.0.0.1:8080/healthEnquanto o ficheiro é carregado, recebe HTTP 503 e este corpo:
{"error":{"code":503,"message":"Loading model","type":"unavailable_error"}}Quando estiver pronto, o corpo será {"status": "ok" }. Em seguida, envie um pedido real.
curl -s http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"local","messages":[{"role":"user","content":"Say hello in five words."}]}'Um objeto JSON com um array choices indica que o servidor está a funcionar. O campo model existe porque os clientes OpenAI o enviam sempre. Este servidor tem um único modelo carregado, pelo que o valor não é usado para selecionar nada.
A API compatível com OpenAI e o que mais está disponível na porta
POST /v1/chat/completions, POST /v1/completions e POST /v1/embeddings são as rotas compatíveis com OpenAI, e GET /v1/models informa o modelo carregado. GET /health é a verificação de prontidão apresentada acima, GET /props devolve as definições atuais do servidor e GET /metrics expõe contadores do Prometheus quando inicia com --metrics.
Qualquer SDK da OpenAI funciona depois de definir o URL base como http://127.0.0.1:8080/v1 e fornecer uma cadeia de chave API não vazia. Essa chave não é validada até definir --api-key manualmente.
Não use a estimativa de throughput de outra pessoa como valor para o seu próprio plano. A velocidade de inferência no CPU depende do número de cores, da largura de banda da memória e dos vizinhos com quem partilha o host. Por isso, meça os tokens por segundo no seu próprio servidor e trate esse resultado como referência. O steal time causado por um vizinho ruidoso manifesta-se aqui como uma velocidade de geração que varia de hora para hora.
Mantenha-o em 127.0.0.1 e coloque um proxy à frente
--host já usa 127.0.0.1 por padrão, portanto o servidor fica inacessível externamente até que esse valor seja alterado. Não o altere. llama-server não tem modelo de utilizadores, limite de pedidos nem um log de auditoria útil. O único controlo integrado é --api-key, que compara uma string. Uma porta de inferência aberta fornece capacidade de computação gratuitamente a quem a encontrar. O mesmo erro cometido com Ollama tem a mesma estrutura: proteger uma API de modelo alojada localmente aplica-se aqui da mesma forma.
Termine o TLS (transport layer security) no nginx e encaminhe para a porta de loopback.
server {
listen 443 ssl;
server_name llm.example.com;
location /v1/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 600s;
}
}proxy_buffering off é necessário para streaming. Com o buffering ativado, o nginx mantém os eventos enviados pelo servidor (SSE) até a resposta terminar. Assim, o cliente fica à espera sem receber dados e depois recebe toda a resposta de uma só vez. proxy_read_timeout 600s cobre gerações longas, porque o valor padrão de 60 segundos transforma uma resposta lenta em 504 Gateway Time-out. Obtenha o certificado com Certbot e Let's Encrypt no nginx.
A unidade systemd
Escreva /etc/systemd/system/llama-server.service.
[Unit]
Description=llama.cpp server
After=network-online.target
Wants=network-online.target
[Service]
User=llama
Group=llama
Environment=LLAMA_ARG_MODEL=/srv/models/gemma-3-1b-it-Q4_K_M.gguf
Environment=LLAMA_ARG_HOST=127.0.0.1
Environment=LLAMA_ARG_PORT=8080
Environment=LLAMA_ARG_CTX_SIZE=4096
Environment=LLAMA_ARG_N_PARALLEL=1
Environment=LLAMA_ARG_THREADS=2
ExecStart=/usr/local/bin/llama-server --no-webui
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
MemoryHigh=3G
MemoryMax=3500M
OOMPolicy=stop
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
[Install]
WantedBy=multi-user.targetAs definições ficam nas linhas Environment= porque llama-server lê as variáveis LLAMA_ARG_* para a maioria das opções, e um argumento da linha de comandos substitui a variável correspondente. Assim, existe um único local para alterar o tamanho do contexto, e ExecStart mantém-se suficientemente curto para ser lido de relance.
ProtectSystem=strict torna todo o sistema de ficheiros só de leitura para esta unidade, o que é adequado porque o servidor apenas lê o modelo. Adicione ReadWritePaths=/srv/models se quiser que o próprio serviço descarregue modelos com -hf. ProtectHome=yes oculta /home e /root, e essa é a segunda razão para manter os modelos em /srv: com ProtectHome ativo, o caminho ~/.cache/llama.cpp predefinido deixa de estar visível para o processo.
sudo systemctl daemon-reload
sudo systemctl enable --now llama-server
systemctl status llama-server
curl -s http://127.0.0.1:8080/health
journalctl -u llama-server -n 50 --no-pagerenable --now é a parte que muitas pessoas ignoram. Sem enable, o servidor deixa de estar disponível depois do próximo reboot. Se quiser executar tarefas agendadas relacionadas com o serviço, como verificar todas as noites se existe uma nova versão, um serviço systemd com um temporizador é o mecanismo adequado.
Decida o que acontece em caso de OOM antes que aconteça
O uso de memória tem duas partes, e elas comportam-se de forma diferente sob um limite. Por predefinição, o ficheiro do modelo é mapeado na memória, por isso as respetivas páginas são suportadas por ficheiro: o kernel pode descartá-las e voltar a lê-las do disco. A cache KV, que é o estado por token mantido pelo servidor para cada conversa ativa, é memória anónima. Não pode ser descartada, por isso é ela que faz com que o processo seja terminado.
É por isso que os dois limites na unidade têm funções diferentes. MemoryHigh=3G é um limite flexível: acima dele, o kernel coloca o cgroup sob pressão de recuperação, pelo que as páginas mapeadas do modelo são expulsas e voltam a ser lidas do disco no token seguinte. O serviço continua a funcionar, mas fica mais lento. MemoryMax=3500M é um limite rígido: acima dele, o processo é terminado, e o journal indica isso claramente.
llama-server.service: A process of this unit has been killed by the OOM killer.Defina --ctx-size manualmente. O valor predefinido é 0, o que corresponde ao contexto com que o modelo foi treinado e, num modelo moderno com contexto longo, aloca uma cache KV muito grande no arranque. O serviço morre antes de atender um único pedido. --parallel multiplica o mesmo custo, porque cada slot mantém o seu próprio estado de conversa. Por isso, mantenha-o em 1 até saber que precisa de concorrência.
Com Restart=on-failure, um serviço terminado volta a arrancar. Se for terminado em todos os arranques, o systemd desiste e systemctl status apresenta start request repeated too quickly. Este é o comportamento correto: um ciclo de reinícios que volta a ler um ficheiro de 12 GB a cada cinco segundos é pior do que uma indisponibilidade. Corrija o limite ou o tamanho do contexto e, em seguida, limpe o estado com sudo systemctl reset-failed llama-server.
Monitorize o valor real com systemctl show llama-server -p MemoryCurrent enquanto um pedido está a ser processado. Limitar a memória e a CPU do processo com systemd aborda estas diretivas com mais detalhe.
Evite usar swap com esta carga de trabalho. Um modelo expulso para swap transforma cada token em leituras do disco com offsets aleatórios. Mapear o ficheiro do modelo na memória produz o mesmo efeito com menos impacto, porque o kernel lê as páginas necessárias diretamente do ficheiro.
Onde o Ollama é a melhor opção
Este é o ponto de decisão. Escolha llama-server quando quiser um processo com flags definidas por si, uma build fixada e um ficheiro escolhido por si, sem alterações subjacentes porque não há mais nada em execução.
Escolha o Ollama quando quiser gestão de modelos: transferir modelos pelo nome, manter vários no disco, descarregar um modelo inativo e atualizar com um único comando em vez de fazer uma nova build. Esse trabalho é real e, de outro modo, teria de o automatizar com scripts. Executar o Ollama numa VPS é o mesmo trabalho, mas com a decisão feita no sentido oposto. Ambos disponibilizam uma API compatível com OpenAI, por isso o código cliente continua a funcionar ao mudar de uma opção para a outra.
Atualizar uma compilação fixada
Substitua bNNNNN pela tag para a qual pretende migrar.
cd llama.cpp
git fetch --tags
git checkout bNNNNN
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-bNNNNN
sudo ln -sfn /usr/local/bin/llama-server-bNNNNN /usr/local/bin/llama-server
sudo systemctl restart llama-serverO binário antigo permanece no disco, portanto um rollback consiste em voltar um ln -sfn para llama-server-b10488 e reiniciar o serviço. Leia as notas da versão antes de fazer a alteração. Os ficheiros GGUF têm versões, e os antigos continuam a ser carregados, mas as flags são renomeadas: --mlock e --no-mmap já estão obsoletas em favor de --load-mode, e um ficheiro de unidade que passe uma flag removida falha no arranque com uma mensagem de argumento não reconhecido.
Modos de falha e mensagens apresentadas
error while loading shared libraries: libllama.so depois de copiar o binário para outro local. A compilação predefinida produz bibliotecas partilhadas juntamente com o binário. Recompile com -DBUILD_SHARED_LIBS=OFF ou copie todo o diretório build/bin.
Illegal instruction (core dumped) no arranque ou no primeiro pedido. O binário foi compilado com GGML_NATIVE ativado para uma CPU diferente da CPU onde está a ser executado. Recompile nesta máquina ou configure com -DGGML_NATIVE=OFF.
c++: fatal error: Killed signal terminated program cc1plus durante a compilação. O compilador foi terminado por consumir memória em excesso. Reduza -j ou adicione swap durante a compilação e remova-a depois.
curl: (7) Failed to connect ... Connection refused a partir do seu portátil. Isto está correto: o servidor escuta no endereço de loopback da VPS. Teste diretamente na VPS ou abra um túnel com ssh -L 8080:127.0.0.1:8080 user@your-vps e utilize http://127.0.0.1:8080 localmente.
HTTP 503 com "message":"Loading model" durante os primeiros segundos ou minutos depois de um reinício. A leitura de um ficheiro com vários gigabytes demora tempo, e o systemd comunica que a unidade está ativa assim que o processo inicia, muito antes de o modelo estar carregado na memória.
Os pedidos ficam bloqueados e depois devolvem 504 Gateway Time-out. O proxy desistiu antes de o modelo terminar. Aumente proxy_read_timeout e desative proxy_buffering para que os tokens sejam enviados ao cliente à medida que são produzidos.
A unidade reinicia repetidamente e depois para com start request repeated too quickly. Algo termina o processo em todos os arranques. Verifique journalctl -u llama-server à procura da linha do OOM killer. Depois reduza --ctx-size, reduza --parallel ou aumente MemoryMax.
FAQ
Devo executar o servidor do llama.cpp ou o Ollama no meu VPS?
Execute llama-server quando quiser fixar uma compilação exata, passar flags exatas e manter um modelo num único ficheiro que nada atualiza sem o seu conhecimento. Use o Ollama quando quiser gestão de modelos e atualizações com um comando, porque descarregar modelos pelo nome, manter vários modelos em disco e descarregar os que estão inativos são tarefas que, de outro modo, teria de automatizar com scripts. Ambos disponibilizam uma API compatível com OpenAI, por isso o código cliente não muda se trocar mais tarde.
Que versão do llama.cpp devo fixar?
Qualquer tag que tenha efetivamente compilado e testado. O llama.cpp cria tags para quase todas as alterações integradas, e os nomes são números de compilação, como b10488, que era a versão mais recente em 18 August 2026. Não existe uma branch estável separada, por isso a versão "atual" muda várias vezes por dia. Faça o clone com --branch <tag>, instale o binário com um nome que contenha essa tag e aponte um symlink para ele. Assim, a atualização e o rollback podem ser feitos com um comando cada.
De quanta RAM precisa o llama-server?
Comece pelo tamanho do ficheiro GGUF. Depois, acrescente a cache KV, que aumenta com --ctx-size e com o número de slots --parallel. Os valores publicados não substituem a medição da sua própria configuração, porque o total depende do modelo, da quantização e do contexto que permitir. Execute systemctl show llama-server -p MemoryCurrent enquanto houver um pedido em processamento e use o número apresentado.
Porque é que /health devolve 503 com "Loading model"?
O processo já iniciou, mas o ficheiro do modelo ainda não está na memória. Por isso, o servidor responde com {"error":{"code":503,"message":"Loading model","type":"unavailable_error"}}. Isto é normal depois de cada reinício e dura o tempo necessário para ler o ficheiro. Só se torna um problema quando um cliente ou proxy trata esse primeiro 503 como uma falha definitiva. Consulte /health repetidamente até devolver {"status": "ok" }.
Posso expor o llama-server diretamente à Internet?
Não faça bind em 0.0.0.0 nem abra a porta. O serviço não tem contas, limitação de taxa nem um log de pedidos adequado para auditoria. A única verificação integrada é --api-key, que compara uma única string. Mantenha o bind predefinido 127.0.0.1, coloque o nginx à frente com TLS e defina também --api-key. Assim, um erro na configuração do proxy não deixa o modelo aberto a qualquer pessoa.