Como rodar o servidor llama.cpp em uma VPS
Compile o llama-server a partir de uma tag fixa, sirva modelos GGUF pela API compatível com OpenAI, use localhost e limite a memória via systemd.
O que você vai 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 do trabalho é operacional: fixar uma versão, manter a porta 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 óbvias, leia primeiro as diferenças entre Ollama e llama.cpp, porque este guia explica precisamente o procedimento que essa comparação deixa de fora.
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 August 2026. Não existe uma branch estável de longa duração. Isso significa que "latest" muda continuamente e que a versão testada é a única que pode suportar. Escolha uma tag, registe-a e use essa mesma string no clone, no nome do binário e nas suas notas.
Cada tag também inclui 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 dele se estiver 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. Numa distribuição mais antiga, falham no arranque com um erro que indica uma versão de GLIBC_ que não está instalada. Compilar a partir do código-fonte demora alguns minutos numa VPS pequena e elimina toda essa classe de problemas. Por isso, esse é o caminho apresentado 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 faz checkout dessa tag e de mais nada, por isso o build não pode divergir enquanto trabalha.
libssl-dev é importante porque a opção LLAMA_OPENSSL está ativada por predefinição. É ela que permite ao binário descarregar modelos por HTTPS mais tarde. Sem os headers, a etapa de configuração falha.
-DBUILD_SHARED_LIBS=OFF fornece um único binário autónomo. O build predefinido coloca as bibliotecas partilhadas junto do executável. Por isso, copiar apenas o executável para /usr/local/bin falha depois com error while loading shared libraries: libllama.so.
-t llama-server compila apenas o destino do servidor. O build predefinido também compila as outras ferramentas e os testes. Num VPS com dois cores, isso representa vários minutos adicionais a processar 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 kernel interrompe o compilador por falta de memória. Reduza o número de tarefas ou adicione swap para o build.
Pode querer alterar uma flag: GGML_NATIVE está ativada por predefinição, por isso o compilador direciona o binário para a CPU exata usada no build. É isso que pretende 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. Esses valores têm de corresponder à tag que foi obtida no checkout. Se não corresponderem, compilou outra coisa. Manter o número no nome do ficheiro e apontar um symlink para ele faz com que uma atualização consista num ln -sfn e num reinício. Um rollback usa o mesmo comando com o número antigo.
Obtenha um modelo GGUF e verifique primeiro o espaço em disco
GGUF é o formato de ficheiro único carregado pelo llama.cpp. Um ficheiro contém os pesos, o tokenizador e os metadados. Não é necessário instalar mais nada. O sufixo do nome do ficheiro indica a quantização, ou seja, a precisão usada para armazenar os pesos: Q4_K_M é uma combinação de 4 bits, Q8_0 usa 8 bits e f16 é o ficheiro de meia precisão sem quantização.
Crie uma conta de serviço e um diretório para o modelo antes de transferir qualquer ficheiro.
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á inacessível em seguida. 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. Assim, o ficheiro da unidade terá um caminho estável para utilizar.
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 é a primeira limitação encontrada pela maioria das pessoas. Estes são os tamanhos publicados para 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. Assim, a escolha do formato altera o tamanho em mais de duas vezes. Um modelo 20B em MXFP4 tem 12.11 GB. Este tamanho não cabe no disco de muitos planos de entrada e, depois, o ficheiro ainda tem de ser lido para a memória.
Verifique df -h antes de cada transferência. Um sistema de ficheiros root 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
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 está a ser carregado, recebe HTTP 503 com 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 uma matriz 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, por isso o valor não é utilizado 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 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 a URL base como http://127.0.0.1:8080/v1 e fornecer uma string de chave de API não vazia. Essa chave não é validada até definir --api-key manualmente.
Não use a alegação de desempenho de outra pessoa como referência para o seu próprio plano. A velocidade de inferência na CPU depende do número de núcleos, 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 considere esse resultado como a referência. O tempo roubado por um vizinho ruidoso aparece aqui como uma velocidade de geração que varia de hora para hora.
Mantenha 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 a partir do exterior até que esse valor seja alterado. Não o altere. llama-server não tem modelo de utilizadores, limite de taxa nem um log de auditoria útil, e o único controlo integrado é --api-key, que compara uma string. Uma porta de inferência aberta fornece capacidade de computação gratuita a quem a encontrar. O mesmo erro cometido com o Ollama tem a mesma estrutura: proteger uma API de modelos alojada localmente aplica-se aqui sem alterações.
Termine o TLS (transport layer security) no nginx e encaminhe os pedidos 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. O cliente fica à espera sem receber dados e depois recebe a resposta completa de uma só vez. proxy_read_timeout 600s abrange gerações demoradas, porque o valor predefinido 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ê variáveis LLAMA_ARG_* para a maioria das opções, e um argumento da linha de comandos substitui a variável correspondente. Assim, há um único local para alterar o tamanho do contexto, e ExecStart continua suficientemente curto para ser lido rapidamente.
ProtectSystem=strict torna todo o sistema de ficheiros apenas 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 não fica 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 timer é 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 pelo 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, por isso as páginas mapeadas do modelo são expulsas e lidas novamente 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, 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 termina 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. Esse é 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, depois, limpe o estado com sudo systemctl reset-failed llama-server.
Monitorize o valor real com systemctl show llama-server -p MemoryCurrent enquanto um pedido estiver em execução. Limitar a memória e a CPU de um processo com systemd explica estas diretivas com mais detalhe.
Evite usar swap nesta carga de trabalho. Um modelo transferido para swap transforma cada token em leituras do disco em offsets aleatórios. O mapeamento do ficheiro do modelo na memória produz o mesmo efeito com menos impacto, porque o kernel lê diretamente do ficheiro as páginas de que precisa.
Quando Ollama é a melhor opção
Este é um ponto de decisão. Escolha llama-server quando quiser um único processo com as flags que definiu, uma build fixada e o ficheiro que escolheu, sem alterações subjacentes porque nada mais está em execução.
Escolha Ollama quando quiser gestão de modelos: obter 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 teria de ser automatizado por si de outra forma. Executar Ollama numa VPS é a mesma tarefa com a decisão feita no sentido oposto. Ambos disponibilizam uma API compatível com OpenAI, por isso o código do cliente continua a funcionar após a mudança em qualquer direção.
Atualizar uma compilação fixada
Substitua bNNNNN pela tag para a qual está a 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 uma reversão consiste num ln -sfn para llama-server-b10488 e num reinício. Leia as notas de versão antes de fazer a alteração. Os ficheiros GGUF têm versões, e os ficheiros antigos continuam a carregar, mas os flags são renomeados: --mlock e --no-mmap já estão obsoletos em favor de --load-mode, e um ficheiro de unidade que passe um flag removido 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 o diretório build/bin completo.
Illegal instruction (core dumped) durante o arranque ou no primeiro pedido. O binário foi compilado com GGML_NATIVE ativado para uma CPU diferente da que o executa. 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 utilizar demasiada memória. Reduza -j ou adicione swap para a compilação e remova-o depois.
curl: (7) Failed to connect ... Connection refused a partir do seu portátil. Isso 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 reporta a unidade como ativa assim que o processo arranca, muito antes de o modelo estar carregado na memória.
Os pedidos ficam pendurados 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 para o cliente à medida que são produzidos.
A unidade reinicia repetidamente e depois para com start request repeated too quickly. Algo termina o processo em cada arranque. Consulte journalctl -u llama-server para localizar a 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 não é atualizado sem o seu conhecimento. Execute o Ollama quando quiser gestão de modelos e atualizações com um comando, porque obter modelos pelo nome, manter vários no disco e descarregar os que estão inativos são tarefas que, de outro modo, teria de automatizar por script. Ambos disponibilizam uma API compatível com OpenAI, por isso o código cliente não muda se trocar mais tarde.
Qual versão do llama.cpp devo fixar?
Qualquer tag que tenha efetivamente compilado e testado. O llama.cpp cria tags em quase todos os merges, e os nomes são números de compilação como b10488, que era a mais recente em 18 August 2026. Não existe uma branch stable separada, por isso a versão "current" 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 para ele através de um symlink, para que a atualização e o rollback possam ser feitos com um comando cada.
De quanta RAM precisa o llama-server?
Comece pelo tamanho do ficheiro GGUF e depois adicione 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.
Por que motivo /health devolve 503 com "Loading model"?
O processo foi iniciado, 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. Faça polling de /health até devolver {"status": "ok" }.
Posso expor o llama-server diretamente à internet?
Não o associe a 0.0.0.0 nem abra a porta. O servidor não tem contas, limitação de taxa nem um log de pedidos adequado para auditoria, e a única verificação integrada é --api-key, que compara uma única string. Mantenha a associação predefinida a 127.0.0.1, coloque o nginx à frente com TLS e defina também --api-key, para que um erro na configuração do proxy não deixe o modelo exposto a qualquer pessoa.