Technical aspects of running local LLMs on FreeBSD

cracauer@

Developer
This is a continuation on my local LLM thread, which is hard to reply to. This one might be easier.



I wanted to waffle a bit more about the hardware involved. You basically have two approaches:
  • Get a fat GPU with a certain amount of VRAM. You will be able to run LLMs up to the VRAM size fast. Overflowing models up to RAM size of the machine will run slow but not necessarily catastrophic if you still have a good chunk in VRAM.
  • Get a machine with integrated GPU that shares RAM with the GPU. This gets you much more memory for the GPU than you can afford with a dedicated GPU. It will run models up to RAM size at mediocre to OK speed.

Cost-wise it is a wash. For $3300-$3500 you can pick between:
  • A used NVidia 5090 with 32 GB VRAM
  • 3x NVidia 3090 and a really fat power supply
  • An AMD Strix Halo (Ryzen AI 395) with 128 GB RAM (shared GPU memory)
  • An Apple Mac with a M4 Max and 128 GB RAM (shared GPU memory)

The Apple Mac doesn't run all models well. Some models are marked as optimized for Metal (Apple's equivalent to Vulkan).

I have been told that Strix Halo prefers to run Mixture of Expert models and doesn't do so well on dense models. I don't have one of those suckers so I can't comment. There's also the aspect of getting FreeBSD to first run on the thing (we have recently seen a failure) and getting Vulkan up.

I have the single NVidia card, which runs out of the box for FreeBSD. But alas everything bigger than 32 GB is slow. There are many interesting models between 32 and 128 GB. But on the other hand, the models that do fit are very fast. You can also use the performance advantage to increase the context size (although that costs further VRAM). I also plan to do extensive experiments with post-training and agents. Speed is more important in that case than when just running a chat through the web browser.

I do not know whether multi-GPU works for one of the llamas on FreeBSD. On paper 3x 3090 looks really attractive since it gives you 72 GB of VRAM for the same price. And functions as heating in winter. Just for starters it might be that multi-GPU in llama.cpp only works with CUDA, not Vulkan.

Theoretically the NVidia GPUs have another advantage: you can run all that GPU software that only has CUDA backends. At the time of this writing CUDA does not work on FreeBSD through Linuxulator, though.

Finally just a word on commercial LLMs: cost-wise it is clearly best to just use up as many of the $20/month plans. They are heavily sponsored. Buying your own hardware can't compete price-wise. But Anthropic might or might not have kicked Claude Code out of that plan:
 
First attempt on my Raptor Lake with UHD730 graphics that should work:
Code:
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = Intel(R) Graphics (RPL-S) (Intel open-source Mesa driver) | uma: 1 | fp16: 1 | bf16: 0 | warp size: 32 | shared memory: 65536 | int dot: 1 | matrix cores: none
Illegal instruction (core dumped)
Do I need a proprietary Intel driver? Never seen it...
Next attempt can be a Ryzen 7 with GTX1050.
 
First attempt on my Raptor Lake with UHD730 graphics that should work:
Code:
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = Intel(R) Graphics (RPL-S) (Intel open-source Mesa driver) | uma: 1 | fp16: 1 | bf16: 0 | warp size: 32 | shared memory: 65536 | int dot: 1 | matrix cores: none
Illegal instruction (core dumped)
Do I need a proprietary Intel driver?

Not sure. What does `vulkaninfo --summary` from port vulkan-tools report? Maybe you only have the Mesa software renderer?
 
Not sure. What does `vulkaninfo --summary` from port vulkan-tools report? Maybe you only have the Mesa software renderer?
Code:
==========
VULKANINFO
==========

Vulkan Instance Version: 1.4.336


Instance Extensions: count = 24
-------------------------------
VK_EXT_acquire_drm_display             : extension revision 1
VK_EXT_acquire_xlib_display            : extension revision 1
VK_EXT_debug_report                    : extension revision 10
VK_EXT_debug_utils                     : extension revision 2
VK_EXT_direct_mode_display             : extension revision 1
VK_EXT_display_surface_counter         : extension revision 1
VK_EXT_headless_surface                : extension revision 1
VK_EXT_surface_maintenance1            : extension revision 1
VK_EXT_swapchain_colorspace            : extension revision 4
VK_KHR_device_group_creation           : extension revision 1
VK_KHR_display                         : extension revision 23
VK_KHR_external_fence_capabilities     : extension revision 1
VK_KHR_external_memory_capabilities    : extension revision 1
VK_KHR_external_semaphore_capabilities : extension revision 1
VK_KHR_get_display_properties2         : extension revision 1
VK_KHR_get_physical_device_properties2 : extension revision 2
VK_KHR_get_surface_capabilities2       : extension revision 1
VK_KHR_portability_enumeration         : extension revision 1
VK_KHR_surface                         : extension revision 25
VK_KHR_surface_protected_capabilities  : extension revision 1
VK_KHR_wayland_surface                 : extension revision 6
VK_KHR_xcb_surface                     : extension revision 6
VK_KHR_xlib_surface                    : extension revision 6
VK_LUNARG_direct_driver_loading        : extension revision 1

Instance Layers:
----------------

Devices:
========
GPU0:
    apiVersion         = 1.3.278
    driverVersion      = 24.1.7
    vendorID           = 0x8086
    deviceID           = 0xa780
    deviceType         = PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
    deviceName         = Intel(R) Graphics (RPL-S)
    driverID           = DRIVER_ID_INTEL_OPEN_SOURCE_MESA
    driverName         = Intel open-source Mesa driver
    driverInfo         = Mesa 24.1.7
    conformanceVersion = 1.3.6.0
    deviceUUID         = 868080a7-0400-0000-0002-000000000000
    driverUUID         = f303ef53-4163-bd95-7437-925e093696ce
GPU1:
    apiVersion         = 1.3.278
    driverVersion      = 0.0.1
    vendorID           = 0x10005
    deviceID           = 0x0000
    deviceType         = PHYSICAL_DEVICE_TYPE_CPU
    deviceName         = llvmpipe (LLVM 15.0.7, 256 bits)
    driverID           = DRIVER_ID_MESA_LLVMPIPE
    driverName         = llvmpipe
    driverInfo         = Mesa 24.1.7 (LLVM 15.0.7)
    conformanceVersion = 1.3.1.1
    deviceUUID         = 6d657361-3234-2e31-2e37-000000000000
    driverUUID         = 6c6c766d-7069-7065-5555-494400000000
 
You have the hardware renderer.

Did you install llama and ggml from packages? Maybe this is an issue of being compiled in a context where native CPU was detected during compilation? Although Raptor Lake should not miss much except avx512.
 
You have the hardware renderer.

Did you install llama and ggml from packages? Maybe this is an issue of being compiled in a context where native CPU was detected during compilation? Although Raptor Lake should not miss much except avx512.
That's possible. I'm not sure about the origin of everything.
It's also not a GENERIC 15 kernel. Some things are changed.I don't think it can be related but it may be a problem. System was still copying so I didn't want to reboot. I'll try a clean kernel and world in a few hours.
 
Qwen 3.6 is out as a dense model (that means that all parameters are active, as oppose to a Mixture of Experts model).

Here is how to find the file to download. Visit the models homepage on Huggingface:

Can't hurt to read some of the drivel on there about the care and feeding of this particular model.

Click on the tab "files and versions":

Pick a quantization and size that fits your computer. Copy the link to the gguf file.

fetch https://huggingface.co/unsloth/Qwen3.6-27B-GGUF/resolve/main/Qwen3.6-27B-Q6_K.gguf

Then start llama.cpp with --model <thatfileondisk>.

There is also an actual homepage for the model:

This particular homepage is not very good. It doesn't have recommendations for parameters like temperature, kv cache and so on.
 
Heads up: version 8895 of llama.cpp has been committed to ports.

This changes the location where llama.cpp stores models automatically downloaded from huggingface (as in post #1 here).
Old cache: ~/.cache/llama.cpp/
New cache: ~/.cache/huggingface/hub

It will automatically move already downloaded models to the new location. If you have different filesystems under there you might be in for a surprise.

In my case the homedir is on NFS and there is a symlink for the old cache to a local NVMe SSD. So it moved things to NFS and afterwards I made to move them back via a new symlink to the SSD. Took 3 hours I'm never getting back.
 
Did you install llama and ggml from packages? Maybe this is an issue of being compiled in a context where native CPU was detected during compilation?
GGML has the ability to detect your CPU at runtime and fallback from whatever it doesn't support. I've patched the ports file to make use of it here:


Note the downgrade of llvm from 22 to 21 in order to workaround a llvm22 regression, which is why I haven't PR-ed it.

Although Raptor Lake should not miss much except avx512.
On current processors, the performance degradation of scheduling avx512 code on an efficiency core instead of a performance core is significantly larger then the degradation of scheduling avx2 code on an e-core instead of a p-core, or in other words, the relative performance to p-cores of e-cores is higher when executing avx2 then it is when execuring avx512. Since FreeBSD-15.0 has no awareness of the cpu design, it will run the inference software on whatever cores are least loaded at the time the inference software is launched, and then will keep it sticked on that cores unless other cores have at least kern.sched.steal_tresh less load on them, which means odds are high that llama.cpp starts on an e-core, allocates mostly e-cores and then stays there. The CPU firmware will then identify llama to be avx heavier then most other workloads and generate an advisory asking the OS to reschedule it onto a p-core, however FreeBSD-15.0 will ignore that advisory and keep it on the e-core(s) nevertheless. So until FreeBSD learns how to address scheduler advisories (currently tracked under D54674 for scheduler advisories in general and D44456 for intel specific Intel Thread Director register processing) there's no point in using avx512 even if your CPU does support it.
 
Great topic for a thread, thanks cracauer@.

Here's some technical aspects for running AI models locally on unbalanced crap hardware with llama-cpp, some random AI models from huggingface and aider.

The crap Hardware:

CPU Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz ~ 2011
16 Gb DD3 RAM @ 1600 ~ 2007
RTX 3060 with 12 Gb NVRAM ~ 2021

Installations:

llama-cpp
git clone https://github.com/ggml-org/llama.cpp[/INDENT][/INDENT]
[INDENT=2][INDENT=2]cd llama.cpp[/INDENT][/INDENT]
[INDENT=2][INDENT=2]mkdir build[/INDENT][/INDENT]
[INDENT=2][INDENT=2]cd build[/INDENT][/INDENT]
[INDENT=2][INDENT=2]cmake -DGGML_VULKAN=1 ..[/INDENT][/INDENT]
[INDENT=2][INDENT=2]cmake --build . --config Release -j 3[/INDENT][/INDENT]
[INDENT=2][INDENT=2]cp llama* lib*.so* ~/.local/bin/

aider - this one is a bit trickier - git clone the source and then pip install in a venv and fix the dependency errors - it can be done but requires some kerfuffling​

Task for AI to do - the prompt, the testcase, whatever you want to call it. The goal is to make AI write the code and test until all errors are gone without intervention.

Create a new C file with a descriptive name. Inside it, implement a function to read the value for the CPU temperature using sysctlbyname with the modern path "dev.cpu.0.temperature". Note that FreeBSD returns this value as an integer in deci-Kelvin (e.g., 3000 means 300.0 Kelvin). Convert this value to Celsius by dividing by 10.0 and subtracting 273.15, then print it out in main() Also in the same code read the values for load average and print those out. If the compilation fails, intercept the errors and fix them.

The random models and how they did.

1)4.36Gqwen2.5-coder-7b-instruct-q4_k_m.ggufsuccess for load average but fail for temperature~ 54 t/s
2)6.87Ggemma-4-12b-Q4_K_M.gguffails caught in a reasoning loop~ 29 t/s
3)6.87Ggemma4-coding-Q4_K_M.ggufsuccess~ 30 t/s
4)7.95GLlama-3-Hercules-5.1-8B-Q8_0.gguffails to create the file~ 35 t/s
5)8.01GCodestral-22B-v0.1-IQ3_XXS.ggufsuccess~ 18 t/s
6)8.37Gqwen2.5-coder-14b-instruct-q4_k_m.ggufsuccess for temperature fail for load averages~ 30 t/s
7)8.43Gmicrosoft_Phi-4-reasoning-Q4_K_M.gguffails by hanging when fixing error~ 32 t/s
8)8.65GDevstral-Small-2-24B-Instruct-2512-UD-Q2_K_XL.ggufsuccess for temperature fails for load average~ 19 t/s
9)8.74GNorth-Mini-Code-1.0-UD-IQ1_M.ggufsuccess~ 54 t/s
a)8.91GQwen3.6-27B-UD-IQ2_XXS.gguffails~ 18 t/s
b)9.11Ggemma4-coding-Q6_K.ggufsuccess for temperature fails for load averages~ 25 t/s
c)9.11GQwen3.5-9B-Q8_0.ggufsuccess for temperature fails for load averages~ 25 t/s
d)14.84GQwen2.5-Coder-32B-Instruct-Q3_K_M.ggufsuccess for temperature fails slightly for load averages (2/3)~ 1 t/s

Warning:
If you run the below scripts you are responsible. You will see llama-cpp warnings like below so make sure you are safe.
srv llama_server: -----------------
srv llama_server: CORS is set to allow all origins ('*') and no API key is set
srv llama_server: this can be a security risk (cross-origin attacks)
srv llama_server: more info: https://github.com/ggml-org/llama.cpp/pull/25655
srv llama_server: -----------------
srv llama_server: -----------------
srv llama_server: the following feature(s) are enabled:
srv llama_server: server tools (experimental)
srv llama_server: do not expose the server to untrusted environments
srv llama_server: -----------------


Comments:
- llama-cpp was installed from ports first, that might have helped to build it from source
- for aider the dependencies that caused errors were installed as pkg and then edited out from requirements.txt
- the reason speed drops for models sized > NVRAM is that the CPU/RAM slows everything down when offloading from NVRAM is being done
- there is margin in the memory management in llama startup script to allow kde and chromium to run at the same time as the ai model
- there is a feeling that Vulkan not always is giving memory back after stopping llama-server with ctrl-c

Script to start llama and pick a model:

sh:
#!/bin/sh

MODELPATH="$HOME/AI-models"
TOTAL_VRAM=12.0      # Physical VRAM in GB
MAX_LOCKED_RAM=10.0  # FreeBSD limit for mlock in GB

# Path to your working Python 3.12 binary inside your venv
PY_BIN="$HOME/venvs/aider-env/bin/python3"

run_llama() {
    FILE_NAME="$1"
    FILE_SIZE_GB="$2"
    FULL_PATH="$MODELPATH/$FILE_NAME"

    echo "──────────────────────────────────────────────────────"
    echo "📊 Analyzing $FILE_NAME ($FILE_SIZE_GB GB)..."

    # Verify NVIDIA GPU and driver status cleanly without screen flickering
    echo "🎮 Verifying NVIDIA GPU status..."
    if [ ! -c /dev/nvidia0 ]; then
        echo "❌ Error: NVIDIA device node (/dev/nvidia0) is missing!"
        echo "   Please ensure the nvidia-driver is loaded properly."
        return 1
    fi

    # Verify that the kernel module is active via sysctl
    NV_VERSION=$(sysctl -n hw.nvidia.version 2>/dev/null)
    if [ -z "$NV_VERSION" ]; then
        echo "❌ Error: NVIDIA kernel module is not active or responsive!"
        return 1
    fi
    echo "✅ GPU Status: NVIDIA Graphics Driver ($NV_VERSION) is active and ready."

    echo "🧠 Reading model built-in metadata live..."

    # Architecture-independent raw binary header parser using native Python struct unpack
    TRAINED_CTX=$("$PY_BIN" -c '
import sys, struct
try:
    with open("'"$FULL_PATH"'", "rb") as f:
        # Read the first 5MB where the GGUF metadata directory lives
        header = f.read(5000000)

        # Locate the context_length byte sequence
        idx = header.find(b"context_length")
        if idx != -1:
            # GGUF keys are followed by a type tag (UINT32 = 6) and the value.
            # We scan a 128-byte window immediately following the key index
            window = header[idx:idx+128]

            # Step through the window and unpack 4-byte little-endian integers (UINT32)
            for i in range(len(window) - 4):
                val = struct.unpack("<I", window[i:i+4])[0]
                # Filter out numbers that match standard context limits (8k to 256k)
                if val in [8192, 16384, 32768, 65536, 131072, 163840, 262144]:
                    print(val)
                    sys.exit(0)
except Exception:
    pass
print("")
')
    # Sanitize the output string in a safe FreeBSD manner
    TRAINED_CTX=$(echo "$TRAINED_CTX" | tr -cd '0-9')
   
    if [ -z "$TRAINED_CTX" ] || [ "$TRAINED_CTX" -lt 2048 ]; then
        TRAINED_CTX=16384
        echo "ℹ  Could not read trained context length. Using safe fallback: $TRAINED_CTX"
    else
        echo "🧠 Model is trained for a max of: $TRAINED_CTX tokens."
    fi

    # Advanced calculation with adaptive cache scaling to guarantee VRAM stability
    EVAL_OPTS=$(awk -v vram="$TOTAL_VRAM" -v ram="$MAX_LOCKED_RAM" -v size="$FILE_SIZE_GB" -v max_ctx="$TRAINED_CTX" '
    BEGIN {
        system_vram_overhead = 1.5;
        available_vram = vram - system_vram_overhead;
       
        if (size < available_vram) {
            ngl = 99;
            ram_fallback = 0;
            b = 512; ub = 256;
            leftover_vram = available_vram - size;
       
            if (size > 6.5) {
                calculated_ctx = int((leftover_vram / 0.08) * 1000);
                type = "q4_0";
            } else {
                calculated_ctx = int((leftover_vram / 0.16) * 1000);
                type = "q8_0";
            }

            if (calculated_ctx >= max_ctx) {
                ctx = max_ctx;
            } else if (calculated_ctx >= 16384) {
                ctx = calculated_ctx;
                if (ctx > 24576) ctx = 24576;
            } else {
                calculated_ctx_q4 = int((leftover_vram / 0.08) * 1000);
                ctx = calculated_ctx_q4;
                if (ctx > max_ctx) ctx = max_ctx;
                type = "q4_0";
            }

            if (ctx < 8192) ctx = 8192;
            if (ctx > max_ctx) ctx = max_ctx;
            ctx = int(ctx / 1024) * 1024;
            if (ctx < 1024) ctx = 1024;
        } else {
            ctx = 16384;
            type = "q4_0";
            if (ctx > max_ctx) ctx = max_ctx;
            kv_overhead = 1.5;
            usable_vram_for_layers = available_vram - kv_overhead;
            if (usable_vram_for_layers < 0) usable_vram_for_layers = 0;
            vram_ratio = usable_vram_for_layers / size;
            approx_total_layers = 64;
            ngl = int(approx_total_layers * vram_ratio);
            if (ngl < 0) ngl = 0;
            if (ngl > 80) ngl = 80;
            ram_fallback = size - usable_vram_for_layers;
            b = 128; ub = 64;
        }
        if (ram_fallback > ram) {
            print "ERROR: Model requires more mlock RAM than your allowed 10 GB!" > "/dev/stderr";
        }
        printf "%d %s %d %d %d\n", ctx, type, b, ub, ngl;
    }')
   
    read -r ctx type b ub ngl <<EOF
$EVAL_OPTS
EOF

    if [ -z "$ctx" ]; then
        echo "❌ Invalid parameters calculated. Aborting."
        return 1
    fi
   
    echo "🎯 Optimal configuration calculated for your hardware:"
    echo "   -> Context Window (-c): $ctx tokens (Model max: $TRAINED_CTX)"
    echo "   -> Cache Memory Type:   $type"
    echo "   -> GPU Layer Offload (-ngl): $ngl (99 = All layers on GPU)"
    echo "   -> Flash Attention:     ON"
    echo "   -> Batch Allocation (-b / -ub): $b / $ub"
    echo "──────────────────────────────────────────────────────"
    echo "🚀 Initializing llama serve..."

    llama-server \
        -t 3 \
        -b "$b" \
        -ub "$ub" \
        -c "$ctx" \
        -np 1 \
        -fa on \
        --load-mode mlock \
        --cache-ram 0 \
        --cors-origins "*" \
        --jinja  \
        --host 0.0.0.0 \
        --port 8080 \
        --tools all \
        --alias kAI \
        --cache-type-k "$type" \
        --cache-type-v "$type" \
        --temp 0.2 \
        --top-p 0.1 \
        --min-p 0.05 \
        -m "$FULL_PATH"

}

# Check if directory exists
if [ ! -d "$MODELPATH" ]; then
    echo "❌ Error: Model directory $MODELPATH does not exist."
    exit 1
fi

echo "Pick an AI model to run: "

# Dynamic discovery mapping array variables on FreeBSD /bin/sh
index=1
chars="123456789abcdefghijklmnopqrstuvwxyz"

# Gather, size-calculate, sort, and loop GGUF models using FreeBSD native POSIX utilities
MATCHED_MODELS=$(find "$MODELPATH" -maxdepth 1 -name "*.gguf" 2>/dev/null | while read -r path; do
    bytes=$(stat -f %z "$path")
    name=$(basename "$path")
    echo "$bytes $name"
done | sort -n)

old_ifs=$IFS
IFS='
'
for line in $MATCHED_MODELS; do
    IFS=$old_ifs
    bytes=$(echo "$line" | awk '{print $1}')
    name=$(echo "$line" | cut -d' ' -f2-)

    size_gb=$(awk -v b="$bytes" 'BEGIN {printf "%.2f", b / 1024 / 1024 / 1024}')

    selector=$(echo "$chars" | cut -c "$index")
    if [ -z "$selector" ]; then
        break
    fi

    eval "MENU_KEY_$selector=\"$name\""
    eval "MENU_SIZE_$selector=\"$size_gb\""

    printf "%s)   %6sG   %s\n" "$selector" "$size_gb" "$name"

    index=$((index + 1))
    IFS='
'
done
IFS=$old_ifs
echo "0) Exit "

printf "Your choice: "
read choice

if [ "$choice" = "0" ] || [ -z "$choice" ]; then
    echo "Exiting."
    exit 0
fi

eval "SELECTED_MODEL=\$MENU_KEY_$choice"
eval "SELECTED_SIZE=\$MENU_SIZE_$choice"

if [ -z "$SELECTED_MODEL" ]; then
    echo "❌ Invalid choice."
    exit 1
fi

run_llama "$SELECTED_MODEL" "$SELECTED_SIZE"

Script to start aider:

sh:
#!/bin/sh

#export HF_HUB_DISABLE_XET=1

# Export env flags
export AIDER_MODEL="openai/kAI"
export AIDER_OPENAI_API_BASE="http://127.0.0.1:8080"
export OPENAI_API_BASE="http://127.0.0.1:8080"
export OPENAI_API_KEY="local-no-key-needed"
export AIDER_SYSTEM_PROMPT="
CRITICAL REQUIREMENT: Always write all of your C code, scripts,
configurations, and experiments into a single file named exactly
"new.c". Never invent other filenames.
You are an expert software developer specializing in FreeBSD.
Always ensure configurations, scripts, and code adhere strictly
to modern FreeBSD best practices and style guides (ZFS, Jails,
bhyve, pkg, and ports). Do not mix Linux commands or paths into
FreeBSD code. Do not mix code for OpenBSD into FreeBSD code.
Always assume the target environment compiles code exclusively with Clang/LLVM.
Ensure all configurations, scripts, and code adhere strictly to modern
FreeBSD best practices. Do not mix Linux commands or GCC-specific flags into code."

#export AIDER_NO_REPO_MAP="true"
#export TREE_SITTER_LANGUAGE_PACK_CACHE="/tmp/empty-syntax-cache"

# Check that AI-model is there
if ! nc -z -w 2 127.0.0.1 8080 > /dev/null 2>&1; then
    echo "❌ Error: llama.cpp is not running on port 8080!"
    exit 1
fi

# Activate the venv
. "$HOME/venvs/aider-env/bin/activate"
export PYTHONPATH="$HOME/aider"
rm -f .env

# Startup aider with the correct map disable flag
"$HOME/venvs/aider-env/bin/aider" \
 --lint-cmd "c: clang -fsyntax-only" \
 --test-cmd "$HOME/scripts/test-runner.sh" \
 --map-tokens 0 \
 --edit-format diff \
 --no-show-model-warnings \
 --yes-always

The test-runner.sh script that will run when /test is run in aider:

sh:
#!/bin/sh
# test_runner.sh

# 1. Automatically grab ALL .c files present in the current directory
ALL_FILES=$(ls *.c 2>/dev/null)

if [ -n "$ALL_FILES" ]; then
    # 2. Pick a name for the binary based on the most recently modified C file
    MAIN_FILE=$(git diff-tree --no-commit-id --name-only -r HEAD | grep "\.c$" | head -n 1)
    if [ -z "$MAIN_FILE" ]; then
        MAIN_FILE=$(ls -t *.c | head -n 1)
    fi
    BINARY="${MAIN_FILE%.c}"

    echo "⚙ Automatically compiling all files ($ALL_FILES) into binary [$BINARY] using clang..."

    # 3. Compile every single C file together
    clang $ALL_FILES -o "$BINARY" && ./"$BINARY"
else
    echo "❌ Error: Aider test runner couldn't locate any active C files."
    exit 1
fi

/grandpa
 
At the time of this writing CUDA does not work on FreeBSD through Linuxulator, though.

You can run Ollama with Cuda on Freebsd using a Rocky Linux Podman container

The Nvidia Driver and Linuxulator libraries are mounted from Freebsd into the Podman container,
and you can start and stop Ollama with a Makefile by running make to start Ollama and make clean to stop it




Code:
[i] Yes Master ? doas podman exec -it -u ${USER} ollama /bin/bash
bash-5.1$ nvidia-smi
Sun Sep  6 15:16:25 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.84                 Driver Version: 595.84         CUDA Version: 13.2     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce GTX 1650        Off |   00000000:01:00.0 Off |                  N/A |
| N/A   72C    P0             16W /   50W |    2547MiB /   4096MiB |     11%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A           26578      C   /usr/lib/ollama/llama-server           2546MiB |
+-----------------------------------------------------------------------------------------+
 
You can run Ollama with Cuda on Freebsd using a Rocky Linux Podman container

Hey NapoleonWils0n - I have had your threads about podman projects on my screen for a week now.

I just need to find a little time and then I will look at the cuda uvm hack and see if I can puzzle it together with llama-cpp. If I understand it correctly llama-cpp is the engine in Ollama but I'm new to all this ai kerfuffling.

I think I saw that you have a thread about finding an x11 guinea pig and I will reply to that since my build-from-ports nvidia drivers forces me to use x11 on this old unbalanced hardware.

/grandpa
 
HI Mate

The Rocky Linux Podman container downloads the Ollama binaries using the Dockerfile

The project lets you create a custom directory to store your own projects that wont be tracked by git

So you can just duplicate the olllama project
and change the Dockerfile to download llama-cpp if you want

Also all the Podman container use networking host
which means they are accessible on 127.0.0.1 from the freebsd side

There are 2 base images you build either X11 or Wayland
then you build the containers like Ollama ontop of those

As Ollama isnt a gui app it wont make much difference
 
Back
Top