Software dockerLocal AIProxmoxServer

Standard llama.cpp for GGUF Models

Set up llama.cpp with CUDA support and run GGUF models locally on the NVIDIA GPU. From the Docker configuration to your first running local LLM.

llamacpp standard 1
10 steps 468 views

In the previous guides we built the technical foundation for our local AI server. Proxmox VE was set up, the NVIDIA GPU was made available to an unprivileged LXC container and Docker was prepared for using the GPU.

Now comes the actual step that turns this foundation into a usable local AI server: llama.cpp.

llama.cpp is a lean inference engine that lets you run large language models locally, directly on your own hardware. A particular advantage is its support for the GGUF format, which is used by many locally run LLMs.

In this guide we set up llama.cpp as a Docker container with CUDA support and then start a first GGUF model via the built-in web server.

We deliberately limit ourselves to the standard version of llama.cpp for now. The goal is a foundation that is as flexible as possible, with which different GGUF models can be tried out and run.

By the end of the guide we will have:

  • set up llama.cpp with CUDA support
  • made the NVIDIA GPU available to llama.cpp
  • started a GGUF model
  • tested the built-in web server
  • learned about important startup parameters
  • laid the foundation for using the llama.cpp API

This gives us not just a single running model, but a flexible basis for various local AI applications.

Note: This guide builds directly on the previous guide on NVIDIA GPU support in a Proxmox LXC container. Docker and the NVIDIA Container Toolkit environment therefore don't need to be set up again.

Tested with the Docker image server-cuda, llama.cpp build 11347, October 2026.

1

Requirements

Before we start with llama.cpp, we first check whether the complete GPU chain works:

Proxmox host → LXC → Docker → NVIDIA GPU

If both of the following tests succeed, the technical foundation for llama.cpp is in place.

Check the NVIDIA GPU in the LXC container
First we check whether the LXC container can access the NVIDIA GPU directly:
We move from the host shell into the LXC container

pct enter 101 

Then we check whether the GPU is available in the LXC container:

nvidia-smi

If the GPU is detected, the NVIDIA model, the driver version and the available VRAM, among other things, should be displayed.

Test the NVIDIA GPU from Docker
Next we check whether Docker can also pass the GPU on to a container:

docker run --rm --gpus all nvidia/cuda:12.8.1-base-ubuntu24.04 nvidia-smi

This starts a temporary CUDA container. It accesses the LXC container's NVIDIA GPU and runs nvidia-smi inside it.
If the GPU is shown here as well, the complete chain works and we can start setting up llama.cpp.

Note: If either of the two tests fails, first check the GPU and Docker setup from the previous guide. In this guide we only start with llama.cpp once both tests succeed.

2

Directory structure for llama.cpp

Before we start llama.cpp as a Docker container, we create a fixed directory structure for the installation and the models.
We deliberately use /opt/llamacpp for this. This keeps the configuration, models and other files in one central place, independent of the actual Docker container.
This is especially handy when more GGUF models are added later or the llama.cpp container needs to be recreated or updated. The models stay in the host file system.

Create the directories

Run the following commands:

mkdir -p /opt/llamacpp/models/huggingface
cd /opt/llamacpp

The structure then initially looks like this:

/opt/llamacpp/
└── models/huggingface/

What are the directories for?
/opt/llamacpp/ – main directory for our llama.cpp installation
models/huggingface/ – cache for models that llama.cpp loads directly from Hugging Face

The model data lives outside the Docker container. This means the container can be recreated or updated at any time without losing the downloaded models.

3

llama.cpp Docker image

llama.cpp can be installed and compiled directly on the system. For our environment, however, we use the Docker version. This way we don't have to compile llama.cpp ourselves and can use a version that is already prepared for CUDA.

The official llama.cpp project provides various Docker images for different use cases. For our NVIDIA setup we use:

ghcr.io/ggml-org/llama.cpp:server-cuda

This is the CUDA variant of the llama.cpp server for NVIDIA GPUs.

We don't need to download the image separately. In the next step we add it to our docker-compose.yml. On the first start with docker compose up -d, Docker automatically checks whether the image is already present. If not, it is downloaded automatically from the GitHub Container Registry.

Important: The Docker image contains the llama.cpp software, but no language model yet. The GGUF models are provided separately later.

4

Create the Docker Compose file

Now we create the Docker Compose configuration for llama.cpp.

In this file we define which llama.cpp image is used, on which port the server is reachable, where the model data is stored and that the container gets access to the NVIDIA GPU.

Create the Compose file

Open the file:

nano /opt/llamacpp/docker-compose.yml

Insert the following content:

services:
  llamacpp:
    image: ghcr.io/ggml-org/llama.cpp:server-cuda
    container_name: llamacpp
    restart: unless-stopped

    ports:
      - "8080:8080"

    volumes:
      - /opt/llamacpp/models/huggingface:/root/.cache/huggingface

    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=compute,utility

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

Save the file with Ctrl + O, confirm with Enter and exit nano with Ctrl + X.

What is configured here?

Image

ghcr.io/ggml-org/llama.cpp:server-cuda

Uses the previously selected llama.cpp image with CUDA support.

Port

8080:8080

The llama.cpp web server will be reachable on port 8080 of the LXC container.

Hugging Face cache and models

/opt/llamacpp/models/huggingface:/root/.cache/huggingface

This is where llama.cpp stores the models loaded directly from Hugging Face. They live in the LXC container's file system and are kept when the container is recreated.

NVIDIA GPU

NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility

This specifies that the NVIDIA GPU is available to the container and that the functions needed for computation are provided. The deploy section additionally assigns the GPU to Docker as a required device, so it doesn't have to be specified manually at every start.

Check the Compose configuration

docker compose config

If Docker prints the configuration without an error message, the Compose file is syntactically correct.

Note: We don't start the container yet. It doesn't have a model yet and would keep restarting because of restart: unless-stopped. It is started in step 5, as soon as a model has been set. You'll find the most important commands (up, down, restart, logs) collected in step 9.

5

Load and start the first GGUF model

Now we start llama.cpp for the first time with a specific GGUF model.

For this setup we use:

unsloth/Muse-Glimmer-30B-GGUF:UD-IQ2_XXS
Why do we choose IQ2_XXS?

Our test platform only has an RTX 3060 with 12 GB of VRAM. With larger language models, it's therefore not only the size of the model itself that matters, but also the additional memory needed for context, KV cache and the running inference process.

As UD-IQ2_XXS, the chosen 30B model is already quantized to around 10.7 GB, so that it basically fits into the available VRAM of a 12 GB GPU.

The low quantization is therefore a deliberate decision here:

12 GB VRAM → largest possible model → smallest possible quantization

This lets us run a considerably larger model on the RTX 3060 than would be possible with, for example, a Q4 quantization of this model size.

Important: The size of the GGUF file is not the same as the total GPU memory required. During execution, llama.cpp needs additional memory for the context and other runtime data. So a 10.7 GB GGUF file doesn't automatically mean that 10.7 GB of VRAM is enough.

Different hardware, different quantization

The choice of quantization therefore depends heavily on your GPU and the available VRAM.

With a single RTX 3090 with 24 GB of VRAM, there's considerably more headroom. With comparable model sizes, less heavily quantized variants can be used as well.

With two RTX 3060s with 12 GB of VRAM each, the available GPU memory can be split across several GPUs. This also opens up possibilities for larger models or higher-quality quantizations.

That's exactly one of the advantages of llama.cpp. We're not tied to a single model or a specific quantization. Depending on the available hardware, different GGUF variants can be used.

For this guide, however, we deliberately choose the variant that fits our 12 GB test platform.

Load the model via Hugging Face

llama.cpp can fetch models directly from Hugging Face. For this we use the -hf parameter.

To do this, we open the docker-compose.yml we created earlier again:

nano /opt/llamacpp/docker-compose.yml

In the existing Compose file, we add the following command block between the environment and deploy sections:

    command: >
      -hf unsloth/Muse-Glimmer-30B-GGUF:UD-IQ2_XXS
      --host 0.0.0.0
      --port 8080
      --n-gpu-layers 99

The Compose file now additionally contains:

services:
  llamacpp:
    image: ghcr.io/ggml-org/llama.cpp:server-cuda
    container_name: llamacpp
    restart: unless-stopped

    ports:
      - "8080:8080"

    volumes:
      - /opt/llamacpp/models/huggingface:/root/.cache/huggingface

    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=compute,utility

    command: >
      -hf unsloth/Muse-Glimmer-30B-GGUF:UD-IQ2_XXS
      --host 0.0.0.0
      --port 8080
      --n-gpu-layers 99

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

What do the additional parameters do?

-hf unsloth/Muse-Glimmer-30B-GGUF:UD-IQ2_XXS

tells llama.cpp to use the specified model directly from the Hugging Face repository. The current llama.cpp server documentation supports this way of fetching models directly from Hugging Face.

The model is stored in the Hugging Face cache we set up earlier:
/opt/llamacpp/models/huggingface/

--host 0.0.0.0

The llama.cpp server becomes reachable on all network interfaces of the container.

--port 8080

The llama.cpp server uses port 8080 inside the container. Our Compose configuration already forwards it to the outside.

--n-gpu-layers 99

This tells llama.cpp to offload as many model layers as possible to the GPU.

The value could also be set to 50, for example. Then not all model weights would be on the GPU, and part of them would have to be processed in system RAM. This usually lowers the tokens/s, because accessing VRAM is much faster than accessing the model parts in system RAM.

With 99 we therefore try to run as many model layers as possible on the RTX 3060.

Note: The actual VRAM usage depends, among other things, on the model, the quantization and the context size chosen later. We check the utilization after starting the model.

Start llama.cpp

After adjusting the Compose file, we first check the configuration:

docker compose config

If no error message appears, we start the container:

docker compose up -d

On the first start, Docker first downloads the llama.cpp image. Depending on your internet connection, this can take some time. You can watch the download in a second shell window, for example with btop.

Then we watch the logs:

docker logs -f llamacpp

Press Ctrl + C to leave the log view; the container keeps running.

On the first start, llama.cpp also downloads the model, around 10.7 GB. Then the model is loaded and the GPU is initialized.

You can see whether the RTX 3060 is being used on the Proxmox host (not in the container) with:

nvtop

As soon as the model is loaded and the server message appears in the log, llama.cpp is reachable at http://192.168.178.134:8080.

🔒 Security: With --host 0.0.0.0, anyone in your network can use the server. In a home network that's usually fine. But never expose port 8080 to the internet in your router. Optionally, you can protect the server with a key: add the line --api-key YOUR_KEY (a long, random password) to the command block. Then enter the key in the web interface settings and send it with curl using -H "Authorization: Bearer YOUR_KEY".

Test the server

1. Web interface: Open http://192.168.178.134:8080 in the browser. There you can chat with the model directly.

2. Check the status:

curl http://192.168.178.134:8080/health

As soon as the model is loaded, {"status":"ok"} is returned. While it's still loading, an error message appears.

3. Chat request via the API:

curl http://192.168.178.134:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Explain in two sentences what GGUF is."}
    ],
    "max_tokens": 200
  }'

The answer comes in OpenAI-compatible format. This means you can also use the server with other programs that support an OpenAI API.

Note: This guide is deliberately about a flexible standard llama.cpp setup that can run different GGUF models. In step 6 we test a second model.

6

Run another GGUF model with different parameters

Now that we've already started a model in the previous step, we test another model with additional parameters.

For this we use:

unsloth/Qwen3.8-27B-GGUF:UD-IQ2_S

This also shows how easy it is to use a different GGUF model with llama.cpp.

Stop the running model

Since the model from the previous step is still running, we first stop the current container:

docker compose down

The model and the stored data are not deleted. Only the running container is stopped and removed.

Adjust the Compose file

First we open our Compose file again:

nano /opt/llamacpp/docker-compose.yml

We replace the previous command block with:

command: >
  -hf unsloth/Qwen3.8-27B-GGUF:UD-IQ2_S
  --host 0.0.0.0
  --port 8080
  --n-gpu-layers 99
  --ctx-size 32768
  --flash-attn on
  --cache-type-k q4_0
  --cache-type-v q4_0
  --jinja
  --temp 1.0
  --top-p 0.95
  --top-k 20
  --min-p 0.00
  --batch-size 512
  --ubatch-size 256
  --repeat-penalty 1.00

Then save the file with Ctrl + O, confirm with Enter and exit nano with Ctrl + X.

Start the model
Then we apply the new configuration:

docker compose up -d

We can watch the logs again with the following command:

docker compose logs -f

While the model is loading, we can watch it with these two commands:

nvidia-smi
btop

Once the server has started successfully, llama.cpp is reachable at:
http://192.168.178.134:8080

This shows that we don't have to reinstall llama.cpp for every model. We stop the running container, change the model and parameters in the Compose file and then start it with the new configuration.

7

Understanding important model parameters

In step 6 we started the Qwen model with several parameters. These settings determine, among other things, the available context, the memory requirement, the speed and the behavior during text generation.

Memory and performance

--n-gpu-layers 99

Offloads as many model layers as possible to the GPU. This makes the fullest possible use of the RTX 3060.

--ctx-size 32768

Sets the maximum size of the prompt context to 32,768 tokens. A larger context needs correspondingly more memory.

--flash-attn on

Enables Flash Attention, an optimized variant of the attention computation that can save memory and compute time depending on the model and hardware.

--cache-type-k q4_0
--cache-type-v q4_0

Store the K and V parts of the KV cache in Q4_0. This reduces the memory requirement of the KV cache.

--batch-size 512

Sets the maximum logical batch size for processing.

--ubatch-size 256

Sets the maximum physical batch size that is actually computed in one processing step.

Text generation

--temp 1.0

Determines the randomness of text generation. Higher values tend to produce more varied output, lower values more predictable output.

--top-p 0.95

Limits the choice of next tokens to the most likely tokens whose cumulative probability reaches 95 %.

--top-k 20

Additionally restricts the choice to the 20 most likely tokens.

--min-p 0.00

Disables min-p filtering. With a value greater than 0, tokens with too low a probability are excluded.

--repeat-penalty 1.00

Sets the penalty for repeated tokens. 1.00 means that no additional repetition penalty is applied.

Chat format

--jinja

For tool calling, i.e. when an agent such as Hermes later needs to call tools via the API, you need this parameter depending on the llama.cpp version. If you plan to use an agent later, include it.

In short

The parameters can roughly be divided into three areas:

GPU and memory: n-gpu-layers, ctx-size, flash-attn, cache-type-k/v
Performance: batch-size, ubatch-size
Text generation: temp, top-p, top-k, min-p, repeat-penalty

Which values make sense depends on the model, the quantization and the available hardware. That's why there isn't a single optimal configuration for every GGUF model.

8

GGUF models, model sizes and quantizations

In the previous steps we've already used different GGUF models. Terms such as 27B, 30B, Q4, IQ3 or IQ2 came up along the way.

To understand these labels correctly, we need to distinguish two things: model size and quantization.

What do 7B, 14B, 27B or 30B mean?
Simply put, the number describes how many parameters the model was trained with.
The B stands for billion parameters.

Examples:
7B → approx. 7 billion parameters
14B → approx. 14 billion parameters
27B → approx. 27 billion parameters
30B → approx. 30 billion parameters

A larger model generally has more parameters that have to be kept in memory while it runs. This usually also increases the memory required.
However, the model size alone doesn't tell you how much VRAM is needed. The quantization used is also decisive.

What is GGUF?
GGUF is a file format for locally run AI models. In addition to the model weights, it contains further information that llama.cpp needs to load and run the model.
An important advantage of GGUF is that the same model can be offered in different quantizations.

What does quantization mean?
Quantization stores the weights of an AI model with fewer bits. This significantly reduces the size of the model, and it needs less RAM and graphics memory.
The advantage: a model that would need a great deal of memory at full precision can, thanks to quantization, also be run on hardware with less VRAM.
The disadvantage: the more heavily a model is quantized, the more the quality of the output can change. Modern quantization methods, however, try to keep this quality loss as small as possible.

In simple terms, you can think of the different quantization levels as a scale:

FP16->Q8->Q6->Q5->Q4->IQ3->IQ2

The more heavily it is quantized, the smaller the model becomes and the less memory is needed. At the same time, quality can decrease compared to a less heavily quantized variant.

The choice is therefore always a trade-off between:
model size ↔ VRAM requirement ↔ speed ↔ quality

An example from our guide
We have an RTX 3060 with 12 GB of VRAM.
That's why for the 30B model from step 5 we use:

Muse-Glimmer-30B-GGUF:UD-IQ2_XXS

A 30B model is considerably larger than, for example, a 7B or 14B model. Thanks to the strong IQ2 quantization, however, the memory requirement is reduced significantly, so we can run the model on our 12 GB GPU.

For the Qwen model from step 6, on the other hand, we use:

Qwen3.8-27B-GGUF:UD-IQ2_S

Here we also use a low quantization, because we're still working with an RTX 3060 with 12 GB of VRAM.

With more VRAM, we have more options.
My workstation even has two RTX 3090s with 24 GB of VRAM each. There I use, for example, the model:

unsloth/Qwen3.8-27B-GGUF:UD-Q8_K_XL

The two GPUs provide a total of 48 GB of VRAM. This allows the 27B model to run with the much higher-quality Q8_K_XL quantization. I also use a context of up to 200k tokens there.

This shows very well what a difference the amount of available VRAM makes:
On the RTX 3060 with 12 GB, we have to quantize larger models more heavily, while two RTX 3090s provide considerably more memory for model weights and a large context.

Multiple GPUs can also be used to run larger models, provided the llama.cpp configuration makes use of the GPUs accordingly.

So it's not simply about always using the largest possible model. What matters is finding a combination of model size, quantization, context size and available hardware that fits the respective use case.

Why we use GGUF
The big advantage of our llama.cpp setup is flexibility. We can use different models and quantizations without having to reinstall the entire environment.

We only change the GGUF model being used and, if necessary, adjust the parameters to the available hardware.

This flexibility is exactly the goal of this guide: to create a general llama.cpp foundation for different local LLM models.

Where do we find suitable GGUF models?
Now that we know what model size, GGUF and quantization mean, one question remains: where do we actually find suitable models, and how do we get them onto our server?
One of the most important places to go is Hugging Face. A great many different AI models are published there and offered by the community in different formats and quantizations.
For our llama.cpp setup, we specifically look for GGUF models.
Another provider where we often find suitable GGUF versions is Unsloth. Among other things, they provide optimized and quantized versions of various models.

Hugging Face
Unsloth

Choosing a suitable model
Once we've decided on a model, we open its model page on Hugging Face.
In the model overview we often find different quantizations of the same model. Depending on the available hardware, we can choose between Q8, Q6, Q5, Q4 or various IQ quantizations, for example.
When we click on one of the offered quantizations, the corresponding model page opens. There we find more information about the selected variant.

At the top right is the "Use this model" button.

When we open it, we can then select "llama.cpp".
There Hugging Face shows us the right command for using the model with llama.cpp.

For example:

-hf unsloth/Qwen3.8-27B-GGUF:Q4_0

We can simply copy this command to the clipboard.

Then we switch back to our server and open the Compose file:

nano /opt/llamacpp/docker-compose.yml

There we paste the copied -hf parameter at the appropriate place in our llama.cpp configuration.
The next time the container starts, llama.cpp then knows which model from Hugging Face to use.

9

Operation and management

Finally, here are the most important Docker commands we need for running and managing llama.cpp.

Start the container

docker compose up -d

Starts the llama.cpp container in the background. If the required Docker image hasn't been downloaded yet, it is fetched automatically.

docker compose down

Stops and removes the llama.cpp container.

The models in the /opt/llamacpp/models directory are kept.

docker compose restart

Restarts the existing container.

docker compose logs -f

Shows the live logs of the llama.cpp container. Press Ctrl + C to leave the log view.

Alternatively, the container can be specified directly:

docker logs -f llamacpp

This command shows the logs of the llamacpp container specifically. This is especially handy when several Docker containers are running on the system at the same time and we only want to watch the output of one particular container.

docker ps

Shows the currently running Docker containers.

When a new version of the llama.cpp Docker image is available, it can first be downloaded:

docker compose pull

Then the container is recreated or started with the new image version:

docker compose up -d

Switch models

The GGUF model in use is defined in the command section of our docker-compose.yml. Open the file:

nano /opt/llamacpp/docker-compose.yml

Adjust the entry under command there, for example:

command: >
  -hf new-model
  --host 0.0.0.0
  --port 8080
  --n-gpu-layers 99

Then apply the change:

docker compose up -d

This starts the llama.cpp container with the new configuration.

Check disk space and delete old models

Each model takes up several GB, and in our setup the container has 40 GB. Check the space with:

df -h /
du -ah /opt/llamacpp/models | sort -rh | head -10

You remove models you no longer need by deleting their folder under /opt/llamacpp/models/huggingface/ (with Hugging Face caches, the folder name usually starts with models--). If you're unsure, better leave it.

10

Conclusion & outlook

With this guide, building on the technical foundation of the Proxmox and NVIDIA guides, we've set up a complete llama.cpp server with CUDA support.

Along the way we saw how GGUF models can be integrated via Docker and run with different parameters. At the same time, we learned the basics of model sizes, quantizations, context sizes and GPU usage.

This third part deliberately forms the general foundation for llama.cpp and GGUF models. In the next part we look specifically at Bonsai 2 and the llama.cpp fork it requires.

Continue with part 4: Bonsai 2 with a llama.cpp fork

← Back to all Local AI guides