I wanted a local LLM on my laptop for one simple reason: some of what I work with (firewall rule exports, config snippets, client notes) should not be pasted into a cloud chatbot. Running models locally means nothing leaves the machine, there is no per-token bill, and I can plug it into other tools through a local API.

The catch is disk space. My C: drive had already filled up once because of Docker, and LLM models are multi-GB files. So the whole setup was built around one rule: the models live on E:, not C:.

This post covers the install, the drive redirect, verifying the GPU is actually used, and what I checked when I came back two months later to upgrade and add a newer model. Every command is here with the real output.

My setup Link to heading

  • Windows laptop, AMD Ryzen 9 5900HS, 32 GB RAM
  • NVIDIA GTX 1650 with 4 GB VRAM
  • Three drives: C: (system, tight on space), D:, and E: (plenty of free space)

The 4 GB VRAM matters a lot for model choice. I’ll get to that.

Why native install and not Docker Link to heading

The obvious choice would be the ollama/ollama Docker image. I skipped it on purpose.

Docker Desktop on Windows stores everything inside a WSL2 virtual disk (ext4.vhdx) that lives on C: by default. It grows as you pull images and it does not shrink on its own. That file is exactly what filled my C: drive before. Putting 15+ GB of models inside it would have repeated the problem.

The native Windows installer gives GPU acceleration out of the box (CUDA libraries are bundled), and it respects an environment variable that tells it where to store models. Simpler and cleaner.

Step 1: Point Ollama at E: before installing Link to heading

Ollama reads the OLLAMA_MODELS environment variable to decide where models go. Set it before installing, so the very first pull lands in the right place.

Open PowerShell and run:

[System.Environment]::SetEnvironmentVariable('OLLAMA_MODELS', 'E:\OllamaModels', 'Machine')

My first attempt failed:

Exception calling "SetEnvironmentVariable" with "3" argument(s): "Requested registry access is not allowed."
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SecurityException

The 'Machine' scope writes a system-wide variable into the registry, and that needs an elevated shell. The window showed C:\WINDOWS\system32 as the path, which made me assume it was an admin window. It wasn’t. The path proves nothing.

The reliable way to check:

([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

True means elevated. False means close the window, go to Start, right-click Windows PowerShell → Run as administrator, and accept the UAC prompt.

Once elevated, the same command went through with no output (that’s success).

You have two choices of scope:

ScopeNeeds adminApplies to
'Machine'YesEvery user and service on the PC
'User'NoOnly your Windows account

Either works for Ollama. I ended up with Machine scope. Remember which one you used, because it matters later (see the upgrade section).

If you prefer the GUI: Win + R → sysdm.cpl → Advanced → Environment Variables → New, name OLLAMA_MODELS, value E:\OllamaModels.

Step 2: Create the folder and install Link to heading

mkdir E:\OllamaModels

Then download the Windows installer from ollama.com/download/windows and run it.

One thing to know: the Ollama program still installs on C: under %LOCALAPPDATA%\Programs\Ollama. The current Windows installer is around 1.6 GB, mostly bundled GPU libraries. That’s unavoidable, but it’s a fixed size. The models are what grow, and those go to E:.

A note on the irm https://ollama.com/install.ps1 | iex style one-liners you’ll see around: check what they’re for before running them. The .exe installer is the straightforward path on Windows.

After install, Ollama starts automatically and sits in the system tray.

Step 3: Verify the path before pulling anything big Link to heading

Open a new PowerShell window (existing windows won’t see a freshly set variable) and check:

PS C:\WINDOWS\system32> echo $env:OLLAMA_MODELS
E:\OllamaModels

Then pull a small model as a test:

PS C:\WINDOWS\system32> ollama pull phi4-mini
pulling manifest
pulling 3c168af1dea0: 100% ▕██████████████████████████████████████████████▏ 2.5 GB
pulling 813f53fdc6e5: 100% ▕██████████████████████████████████████████████▏  655 B
pulling fa8235e5b48f: 100% ▕██████████████████████████████████████████████▏ 1.1 KB
pulling 8c2539a423c4: 100% ▕██████████████████████████████████████████████▏  411 B
verifying sha256 digest
writing manifest
success

And confirm the files actually landed on E:

PS C:\WINDOWS\system32> dir E:\OllamaModels

    Directory: E:\OllamaModels

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        22-07-2026     14:59                blobs
d-----        22-07-2026     14:59                manifests

blobs holds the model weights, manifests holds the metadata. To see the real file sizes:

Get-ChildItem E:\OllamaModels\blobs | Sort-Object Length -Descending |
  Select-Object Name, @{N='SizeGB';E={[math]::Round($_.Length/1GB,2)}}

You should see one blob of about 2.5 GB. If the folder is empty and your C: drive just lost 2.5 GB, the variable wasn’t picked up. Quit Ollama from the tray, reopen it, and check again.

Step 4: Choosing models for 4 GB VRAM and 32 GB RAM Link to heading

This is where most guides lose people. How a model runs depends on whether it fits in GPU memory:

  • Fits fully in VRAM: fast, the GPU does all the work.
  • Doesn’t fit: Ollama splits the layers between GPU and system RAM. It still works, but it gets slower the more sits in RAM.
  • Mostly in RAM: works if you have the RAM, but slow.

With 4 GB VRAM and 32 GB RAM, this is the lineup I settled on:

ModelSizeWhere it runs on my laptopUse
phi4-mini2.5 GBFully on GPUFast daily driver
qwen2.5:7b4.7 GBSplit GPU + RAMBetter quality, still usable
phi4 (14B)9.1 GBMostly RAMSmarter, slow
nomic-embed-text274 MBGPUEmbeddings for RAG (document chat)
ollama pull qwen2.5:7b
ollama pull phi4
ollama pull nomic-embed-text

What I deliberately skipped: dense 27B to 32B models. They technically fit in 32 GB of RAM, but on CPU they run at a couple of tokens per second. Not worth it.

Step 5: Prove the GPU is actually being used Link to heading

Don’t assume. Check. Open two PowerShell windows.

Window 1:

nvidia-smi -l 2

Window 2:

ollama run phi4-mini "Explain zero trust in 2 sentences"

In the nvidia-smi process list you want to see Ollama’s runner:

|    0   N/A  N/A   4056    C   ...a\lib\ollama\llama-server.exe      N/A |

and memory usage climbing on the GPU line:

|   2364MiB /   4096MiB |

About 2.4 GB of VRAM used with phi4-mini loaded, so the model is on the GPU.

One thing that confused me: the snapshot sometimes shows GPU-Util 0% even with the model loaded. That’s normal. Utilization only spikes while tokens are being generated, and Ollama keeps a model in memory for about 5 minutes after your last prompt, doing nothing. Loaded is not the same as busy.

If llama-server.exe never shows up and your RAM usage jumps instead, Ollama fell back to CPU. See the note about AMD integrated graphics below.

Two months later: upgrading Ollama Link to heading

I came back to add a newer model and expected to run an upgrade first. First step, check the version:

PS C:\WINDOWS\system32> ollama --version
ollama version is 0.34.4

It was already newer than the latest release I had read about. Ollama on Windows updates itself in the background. When an update is ready, the tray icon offers Restart to update. If you ever need to force it, download the latest installer from ollama.com and run it over the top. Your models are not touched.

So the “upgrade” turned into a post-upgrade health check, which is the part that actually matters.

Check 1: Models still there Link to heading

PS C:\WINDOWS\system32> ollama list
NAME                       ID              SIZE      MODIFIED
nomic-embed-text:latest    0a109f422b47    274 MB    2 months ago
phi4:latest                ac896e5b8b34    9.1 GB    2 months ago
qwen2.5:7b                 845dbda0ea48    4.7 GB    2 months ago
phi4-mini:latest           78fad5d182a7    2.5 GB    2 months ago

All four present.

Check 2: The models path. This one gave me a scare Link to heading

PS C:\WINDOWS\system32> [Environment]::GetEnvironmentVariable("OLLAMA_MODELS", "User")
PS C:\WINDOWS\system32>

Empty. My first thought was that the update had wiped the variable and my next 13 GB download was about to land on C:.

Before panicking, check the other scope:

PS C:\WINDOWS\system32> [Environment]::GetEnvironmentVariable("OLLAMA_MODELS", "Machine")
E:\OllamaModels

It had been set at Machine scope all along. I was looking in the wrong place.

Lesson: query the scope you actually used, or check both. $env:OLLAMA_MODELS shows the effective value in the current session, whichever scope it came from.

Check 3: Where the bytes actually are Link to heading

Don’t trust the variable alone. Measure both locations:

PS C:\WINDOWS\system32> Get-ChildItem "E:\OllamaModels" -Recurse -File -ErrorAction SilentlyContinue |
  Measure-Object Length -Sum | Select-Object @{N='E_GB';E={[math]::Round($_.Sum/1GB,2)}}

 E_GB
 ----
15.37

PS C:\WINDOWS\system32> Get-ChildItem "$env:USERPROFILE\.ollama\models" -Recurse -File -ErrorAction SilentlyContinue |
  Measure-Object Length -Sum | Select-Object @{N='C_GB';E={[math]::Round($_.Sum/1GB,2)}}
PS C:\WINDOWS\system32>

15.37 GB on E:, nothing on C: (the default location %USERPROFILE%\.ollama\models doesn’t even exist). If you’re wondering why 15.37 doesn’t match the ~16.6 GB that ollama list adds up to: ollama list uses decimal GB, and dividing by 1GB in PowerShell gives binary GiB. Same data.

Check 4: Free disk space and free RAM Link to heading

PS C:\WINDOWS\system32> Get-PSDrive E | Select-Object @{N='FreeGB';E={[math]::Round($_.Free/1GB,1)}}

FreeGB
------
 186.1

PS C:\WINDOWS\system32> Get-CimInstance Win32_OperatingSystem |
  Select-Object @{N='FreeRAM_GB';E={[math]::Round($_.FreePhysicalMemory/1MB,1)}}

FreeRAM_GB
----------
      18.5

Plenty of disk. The RAM number matters for the next step, because a model running mostly from RAM needs that RAM free when it loads.

Check 5: GPU still in use after the update Link to heading

Same nvidia-smi test as Step 5. Worth repeating after every update, especially on AMD laptops. Ryzen laptops have integrated Radeon graphics, and there are reported cases where the AMD iGPU triggers Ollama’s ROCm detection, fails, and pushes everything to CPU even though a perfectly good NVIDIA GPU is present. Mine was fine: llama-server.exe on the GTX 1650 as before.

Adding a newer model: gpt-oss:20b Link to heading

The newer models worth trying on this kind of hardware are Mixture-of-Experts (MoE) models. They’re large on disk, but only a small slice of the model is active for each token, so they run far better from RAM than a dense model of the same size. OpenAI’s open-weight gpt-oss:20b is one of them, and it’s commonly recommended for machines with 16 GB+ RAM.

PS C:\WINDOWS\system32> ollama pull gpt-oss:20b
pulling manifest
pulling e7b273f96360: 100% ▕██████████████████████████████████████████████▏  13 GB
verifying sha256 digest
writing manifest
success

Then verify it landed on E: by measuring again:

PS C:\WINDOWS\system32> ollama list
NAME                       ID              SIZE      MODIFIED
gpt-oss:20b                17052f91a42e    13 GB     50 seconds ago
nomic-embed-text:latest    0a109f422b47    274 MB    2 months ago
phi4:latest                ac896e5b8b34    9.1 GB    2 months ago
qwen2.5:7b                 845dbda0ea48    4.7 GB    2 months ago
phi4-mini:latest           78fad5d182a7    2.5 GB    2 months ago

PS C:\WINDOWS\system32> Get-ChildItem "E:\OllamaModels" -Recurse -File |
  Measure-Object Length -Sum | Select-Object @{N='E_GB';E={[math]::Round($_.Sum/1GB,2)}}

 E_GB
 ----
28.22

15.37 → 28.22 GB. The 12.85 GB difference is the new model, on E:, exactly where it should be.

To compare models fairly, run the same prompt through each with --verbose, which prints load time and tokens per second at the end:

ollama run qwen2.5:7b --verbose "A firewall rule allows any-any from Trust to Untrust with application-default. What are the risks and how would you tighten it?"
ollama run gpt-oss:20b --verbose "A firewall rule allows any-any from Trust to Untrust with application-default. What are the risks and how would you tighten it?"

The number to look at is eval rate. Use a prompt from your own field. You’ll judge the answer quality much better than any benchmark can.

Some models look like they fit but don’t. Gemma 4’s “e2b” and “e4b” variants sound tiny, but they’re 7.2 GB and 9.6 GB on disk because of the vision and audio parts. Neither runs fully on a 4 GB card. Check the actual size on the Ollama library page, not the name.

Already installed to C:? Moving existing models Link to heading

I set the variable before installing, so I didn’t need this. If you didn’t, this is the usual way to move models without re-downloading:

# 1. Quit Ollama from the system tray first, then:
robocopy "$env:USERPROFILE\.ollama\models" "E:\OllamaModels" /E /MOVE

# 2. Set the variable (User scope, no admin needed)
[System.Environment]::SetEnvironmentVariable('OLLAMA_MODELS', 'E:\OllamaModels', 'User')

# 3. Reopen Ollama from the Start menu, open a NEW PowerShell window, and check
ollama list

If ollama list shows your models, it worked.

What I learned Link to heading

  • Set OLLAMA_MODELS before the first pull. It’s much easier than moving 15 GB later.
  • A system32 prompt doesn’t mean admin. Use the IsInRole check.
  • Know which scope you set. Checking User when you set Machine looks exactly like “the variable is gone”.
  • Measure, don’t assume. A one-line Measure-Object on both drives settles “where did my models go” in seconds.
  • Ollama on Windows auto-updates. Check the version before planning an upgrade.
  • Re-check the GPU after every update, especially on AMD laptops with integrated graphics.
  • VRAM decides speed, RAM decides what’s possible. On a 4 GB card, the newer MoE models are the ones that make the extra RAM useful.

Cheat sheet Link to heading

Ollama CLI Link to heading

ollama --version                  # installed version
ollama list                       # models on disk
ollama ps                         # models loaded in memory right now (and GPU/CPU split)
ollama pull <model>               # download or update a model
ollama run <model>                # interactive chat
ollama run <model> --verbose "prompt"   # one-shot prompt + speed stats
ollama show <model>               # details: parameters, context length, capabilities
ollama show <model> --modelfile   # the underlying Modelfile
ollama stop <model>               # unload from memory now
ollama rm <model>                 # delete from disk
ollama cp <model> <new-name>      # copy/alias a model

ollama ps is the quickest way to see whether a loaded model is on the GPU. The PROCESSOR column shows something like 100% GPU or 48%/52% CPU/GPU.

Environment variables Link to heading

# Effective value in this session
$env:OLLAMA_MODELS

# Check each scope
[Environment]::GetEnvironmentVariable("OLLAMA_MODELS", "User")
[Environment]::GetEnvironmentVariable("OLLAMA_MODELS", "Machine")

# Set (User = no admin, Machine = admin)
[System.Environment]::SetEnvironmentVariable('OLLAMA_MODELS', 'E:\OllamaModels', 'User')

# List every Ollama-related variable in this session
Get-ChildItem Env: | Where-Object Name -like "OLLAMA*"

Other variables worth knowing: OLLAMA_KEEP_ALIVE (how long a model stays loaded, e.g. 10m, or 0 to unload right away) and OLLAMA_HOST (bind address; the default is 127.0.0.1:11434, only change it if you actually want other machines on your network to reach it). Restart Ollama after changing any of them.

Is Ollama running? Link to heading

Get-Process ollama*                                    # processes
Get-NetTCPConnection -LocalPort 11434 -State Listen    # API port listening?
Invoke-RestMethod http://localhost:11434/api/version   # API responding?
Invoke-RestMethod http://localhost:11434/api/tags      # models, via the API

Logs Link to heading

explorer $env:LOCALAPPDATA\Ollama                      # app.log, server.log live here
Get-Content "$env:LOCALAPPDATA\Ollama\server.log" -Tail 50
Get-Content "$env:LOCALAPPDATA\Ollama\server.log" -Tail 200 | Select-String -Pattern "cuda|gpu|rocm"

The GPU lines in server.log are the first place to look when a model is unexpectedly slow.

GPU Link to heading

nvidia-smi                        # snapshot: VRAM, utilization, processes
nvidia-smi -l 2                   # refresh every 2 seconds
nvidia-smi --query-gpu=name,memory.used,memory.total,utilization.gpu,temperature.gpu,power.draw --format=csv -l 5
nvidia-smi --query-gpu=compute_cap --format=csv   # compute capability (Ollama needs 5.0+)

Disk and memory Link to heading

# Free space on a drive
Get-PSDrive E | Select-Object @{N='FreeGB';E={[math]::Round($_.Free/1GB,1)}}

# Free space on all drives
Get-PSDrive -PSProvider FileSystem | Select-Object Name,
  @{N='UsedGB';E={[math]::Round($_.Used/1GB,1)}}, @{N='FreeGB';E={[math]::Round($_.Free/1GB,1)}}

# Total size of a folder
Get-ChildItem "E:\OllamaModels" -Recurse -File | Measure-Object Length -Sum |
  Select-Object @{N='GB';E={[math]::Round($_.Sum/1GB,2)}}

# Largest model blobs
Get-ChildItem E:\OllamaModels\blobs | Sort-Object Length -Descending |
  Select-Object -First 10 Name, @{N='SizeGB';E={[math]::Round($_.Length/1GB,2)}}

# Free RAM
Get-CimInstance Win32_OperatingSystem |
  Select-Object @{N='FreeRAM_GB';E={[math]::Round($_.FreePhysicalMemory/1MB,1)}},
                @{N='TotalRAM_GB';E={[math]::Round($_.TotalVisibleMemorySize/1MB,1)}}

# Top memory users
Get-Process | Sort-Object WorkingSet64 -Descending |
  Select-Object -First 10 Name, @{N='RAM_GB';E={[math]::Round($_.WorkingSet64/1GB,2)}}

Housekeeping Link to heading

# Am I admin?
([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

# Reclaim space from old Windows update components (admin)
DISM /Online /Cleanup-Image /StartComponentCleanup

# Remove a model you no longer use
ollama rm phi4

Post-upgrade checklist Link to heading

ollama --version
ollama list
$env:OLLAMA_MODELS
Get-ChildItem "E:\OllamaModels" -Recurse -File | Measure-Object Length -Sum |
  Select-Object @{N='E_GB';E={[math]::Round($_.Sum/1GB,2)}}
ollama run phi4-mini "hello"      # then check: ollama ps

If all five look right, the upgrade is done. Next up for me is connecting this to AnythingLLM so I can chat with my own documents locally. That’ll be its own post.