Ollama systemd Service: Env Vars That Don't Work
Want to go deeper than this article?
Free account unlocks the first chapter of all 25 courses — RAG, agents, MCP, voice AI, MLOps, real GitHub repos.
Ollama’s running. Here’s what to build with it. Go from “ollama run” to RAG apps, agents, and fine-tuned models — structured and hands-on. First chapter free.
On Linux, export OLLAMA_HOST=0.0.0.0 in your shell has no effect on Ollama, because the official installer registers a systemd service that runs the daemon as a separate ollama system user with its own environment. Your terminal and that process share nothing. The only environment the daemon reads is what the unit file declares, so the fix is a drop-in override: sudo systemctl edit ollama, add an Environment= line under a [Service] header, then sudo systemctl daemon-reload && sudo systemctl restart ollama. Verify it landed with journalctl -u ollama --no-pager | grep "server config" — Ollama prints its entire resolved configuration at startup, and if your variable is not in that line, it never arrived.
This is the mechanism underneath a surprising number of separate-looking Ollama problems. "OLLAMA_MODELS is ignored", "OLLAMA_ORIGINS does not work", "my proxy settings are not picked up", "the GPU is used when I run it manually but not as a service" — on Linux these are frequently the same root cause wearing four different symptoms, and none of them are bugs.
The unit contents, the user creation and the install paths below were read from scripts/install.sh on main on 23 August 2026. The systemd behaviour is quoted from the systemd manual pages. Issue numbers link to the tracker so you can check current state.
Why Does export OLLAMA_HOST Do Nothing
A process inherits its environment from its parent, and your shell is not the daemon's parent — PID 1 is. When you type export OLLAMA_HOST=0.0.0.0:11434, you modify the environment of your shell and everything it launches afterwards. The Ollama daemon was launched by systemd, long before you opened that terminal, from a completely different environment.
Three consequences that catch people:
ollama servein your terminal is a different server. It picks up your exported variables perfectly. It also collides with the running service on port 11434, which is why the attempt usually ends inbind: address already in userather than a working test.- Restarting does not help.
systemctl restart ollamare-launches the daemon from systemd's environment, not yours. The variable is just as absent the second time. - Putting it in
.bashrc,.profileor/etc/environmentdoes not help either. The first two are shell files the daemon never sources./etc/environmentis read by PAM at login, and a system service started at boot does not log in.
There is one more layer that surprises people the first time: the CLI and the server share the OLLAMA_HOST name but use it for different purposes. Exported in your shell, it tells ollama where to connect. Set in the unit, it tells the daemon where to bind. Setting it in the wrong place produces a client that dials somewhere nothing is listening, which is covered in detail in our connection refused on port 11434 walkthrough.
Reading articles is good. Building is better.
Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.
Where Does the Installer Actually Put the Unit File
/etc/systemd/system/ollama.service — which is the administrator's directory, not the vendor's, and that choice causes real problems. It matters for two reasons: it is where you go to read what is actually configured, and it is why some perfectly reasonable systemd commands fail on this service.
The clearest symptom is on the tracker right now. Issue #17345, "systemctl mask ollama fails ('File already exists'), installer writes the unit into /etc/systemd/system" (opened 23 July 2026, open at the time of writing), reports that:
Failed to mask unit: File '/etc/systemd/system/ollama.service' already exists
That error is systemd behaving exactly as documented. Per systemctl(1), masking "will link these unit files to /dev/null, making it impossible to start them", and it "creates a symlink under the unit's name in /etc/systemd/system/". A regular file is already sitting at that exact path, so the symlink cannot be created. The same report notes that systemctl disable ollama does not survive a reinstall, because the install script runs systemctl enable ollama unconditionally every time. A closed pull request, #17363, proposed moving the unit to the vendor directory /usr/local/lib/systemd/system/ so that mask and disable persist.
This is a long-running theme rather than a one-off. #13178 ("Install script overwrites systemd config"), #14647 ("Installer should not overwrite /etc/systemd/system/ollama.service"), #8389 and #8048 all describe an upgrade wiping hand-edited unit files. This is the single strongest argument for using a drop-in instead of editing the unit directly — a drop-in lives in a different file that the installer does not write.
What Is Inside the Unit Ollama Ships
Here is what the install script writes, verbatim:
[Unit]
Description=Ollama Service
After=network-online.target
[Service]
ExecStart=/usr/local/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3
Environment="PATH=$PATH"
[Install]
WantedBy=default.target
(The ExecStart path is built from the script's install directory variable; /usr/local/bin/ollama is where the standard install puts the binary. Confirm yours with systemctl cat ollama.)
Five things in those twelve lines explain a lot of downstream behaviour:
| Line | What it means for you |
|---|---|
User=ollama / Group=ollama | The daemon is not you. Every file it touches is subject to that user's permissions, and every environment variable must be declared here |
Environment="PATH=$PATH" | The installer freezes the installing shell's PATH into the unit. If you installed with sudo, that is root's PATH, captured at that moment and never updated |
Restart=always with RestartSec=3 | A broken configuration produces a restart loop every three seconds, not a clean stop. Your journal fills with the same error repeatedly |
After=network-online.target | An ordering directive only. There is no matching Wants=, so it orders Ollama after that target if something else pulls it in, and does not pull it in by itself |
WantedBy=default.target | Enablement hangs off the default target rather than multi-user.target — worth knowing if you are hunting for the enable symlink |
The user itself is created by the script with:
useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama
A system account (-r), with no login shell (-s /bin/false), its own group (-U), and a home directory at /usr/share/ollama. That home is why the default model location on Linux is /usr/share/ollama/.ollama/models rather than something under /home. The script then adds ollama to the render and video groups when they exist, and adds your user to the ollama group — which is what makes the models directory readable from your account after an install.
The open pull request #14895 proposes having the service wait for graphical.target, and #16191 ("Linux Install Script Assumes systemd", closed) is the reminder that on a non-systemd distro none of this applies and you are managing the process yourself.
How Do You Set an Environment Variable the Right Way
Use a drop-in. Never edit /etc/systemd/system/ollama.service directly, because the next installer run will overwrite it.
sudo systemctl edit ollama
That opens an editor on a new, empty file. systemctl(1) describes the command as "Edit or replace a drop-in snippet or the main unit file, to extend or override the definition of the specified unit", with the default drop-in name being override.conf. The file it creates is:
/etc/systemd/system/ollama.service.d/override.conf
Put a [Service] header in it — the section header is mandatory and omitting it is the most common mistake — followed by one Environment= line per variable:
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_ORIGINS=chrome-extension://*"
Environment="OLLAMA_KEEP_ALIVE=30m"
Then apply it:
sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl edit reloads the configuration for you when the editor exits, but running daemon-reload explicitly costs nothing and covers the case where you created the file with a text editor instead. daemon-reload is documented as "Reload the systemd manager configuration. This will rerun all generators, reload all unit files, and recreate the entire dependency tree" — note that it reloads configuration, it does not restart your service. Both commands are required.
Syntax rules that actually bite:
- The quotes go around the whole assignment, not the value:
Environment="OLLAMA_HOST=0.0.0.0:11434", notEnvironment=OLLAMA_HOST="0.0.0.0:11434". Both forms are accepted by systemd, but the second leaves literal quote characters in the value on some systemd versions. Ollama trims surrounding quotes on read for several variables, which hides the mistake right up until it does not. - One variable per line is safer than several. Multiple assignments on one
Environment=line are legal but the quoting rules get subtle fast. - No shell. There is no command substitution, no globbing, no tilde expansion.
Environment="OLLAMA_MODELS=~/models"sets the literal string~/modelsand the daemon will try to create a directory with that name. - Drop-ins add to the main unit, they do not replace it. From
systemd.unit(5): "All files with the suffix '.conf' from this directory will be merged in the alphanumeric order and parsed after the main unit file itself has been parsed." So the installer'sEnvironment="PATH=..."survives your override rather than being replaced by it, which is what you want.
If you need to change something that is not a variable — User=, ExecStart=, Restart= — the same drop-in works, with one caveat covered in the last section.
Reading articles is good. Building is better.
Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.
How Do You Prove the Daemon Received It
Four checks, in increasing order of how much they prove. Run them in order and stop at the first one that shows something wrong.
| Command | What it shows | What "good" looks like |
|---|---|---|
systemctl cat ollama | The main unit plus every drop-in, in merge order. systemctl(1): "Show backing files of one or more units. Prints the 'fragment' and 'drop-ins' (source files) of units" | Your override.conf appears, with a [Service] header above your lines |
systemctl show ollama --property=Environment | The environment list systemd has parsed and will apply | Your variable, correctly split into name and value |
sudo tr '\0' '\n' < /proc/$(pgrep -x ollama)/environ | The live process's actual environment, straight from the kernel | Your variable present in the running process, not just in config |
journalctl -u ollama --no-pager | grep "server config" | Ollama's own resolved view of every setting | Your value inside the map, with defaults filled in around it |
That last one is the strongest and the least known. Ollama logs its full configuration at startup with slog.Info("server config", "env", envconfig.Values()) in server/routes.go, so a single grep tells you what the server believes, rather than what you configured. If the variable is present in systemctl show and absent from server config, you did not restart the service after daemon-reload.
The journal invocation for watching a start attempt live is the one from Ollama's official troubleshooting doc:
journalctl -u ollama --no-pager --follow --pager-end
Two failure modes this sequence catches immediately. If systemctl cat does not show your drop-in at all, you probably ran systemctl edit ollama.service on a machine where the unit is named something else, or your editor exited without saving. If it shows the drop-in but systemctl show has an empty Environment=, your [Service] header is missing or misspelled.
Which OLLAMA Variables Belong in the Unit
Any OLLAMA_* variable that changes daemon behaviour goes in the unit. Variables that change client behaviour belong in your shell. Getting this backwards is the source of the "server works, CLI does not" class of report.
| Variable | Goes in the unit? | Why |
|---|---|---|
OLLAMA_HOST | Yes, for the bind address | The daemon reads it to decide what to listen on. Setting it in your shell instead tells the CLI where to dial |
OLLAMA_ORIGINS | Yes | Read once at server startup to build the CORS allow-list. See Ollama 403 Forbidden and OLLAMA_ORIGINS for what to put in it |
OLLAMA_MODELS | Yes | The daemon owns the model store. Permissions matter — see the next section, and our OLLAMA_MODELS not working page for the non-Linux cases |
OLLAMA_KEEP_ALIVE | Yes | Controls how long a model stays resident in the daemon |
OLLAMA_CONTEXT_LENGTH | Yes | A server-side default. The open docs PR #15851 exists specifically to add the systemd instructions for it |
OLLAMA_DEBUG | Yes, temporarily | Verbose GPU discovery and load logging, which lands in the journal |
HTTPS_PROXY / HTTP_PROXY / NO_PROXY | Yes | ollama pull runs inside the daemon, so the daemon needs the proxy, not your shell. This is why proxy settings that work for curl do nothing for ollama pull |
OLLAMA_HOST for the CLI | No | Export this in your shell when you want the client to talk to a remote server |
The proxy row deserves emphasis because it is so counter-intuitive. When you run ollama pull, the CLI sends an API request and the server process performs the download. A proxy exported in your terminal is not in that process's environment, so the pull goes direct and hangs or fails on a network that requires the proxy.
Why Does the Service Die After You Set OLLAMA_MODELS
Because the ollama user has to be able to reach and write that path, and "the directory is 777" is not the same as "the path is traversable". This is the highest-frequency start failure after an override edit, and the diagnosis is almost always in a parent directory rather than the one you fixed.
The official FAQ states the requirement plainly: if you change OLLAMA_MODELS, "the ollama user needs read and write access to the specified directory".
Issue #9335 ("systemd service fails to start", closed) is the textbook case. The reporter pointed OLLAMA_MODELS at a path on a RAID mount and got, in the journal:
Error: mkdir /mnt/raid_disk/ollama: permission denied
ollama.service: Main process exited, code=exited, status=1/FAILURE
ollama.service: Failed with result 'exit-code'.
The target directory /mnt/raid_disk/ollama/ was owned by ollama:ollama with mode 777. The failure was one level up: the parent /mnt/raid_disk was drwx----w-, so the ollama user could not traverse into it to reach the child at all. Permissions on a leaf directory are irrelevant if execute permission is missing anywhere along the path.
The diagnostic that finds this in one command is namei, which walks each component of a path and prints its mode and owner:
namei -l /mnt/raid_disk/ollama
Read down the output looking for the first line where ollama has no x bit through user, group or other. That is your culprit. Then fix ownership and traversal together:
sudo chown -R ollama:ollama /mnt/raid_disk/ollama
sudo chmod o+x /mnt/raid_disk
Two related traps on the same theme. Home directories on recent Debian and Ubuntu are created with mode 750, so OLLAMA_MODELS=/home/you/models fails for exactly this reason even though the models directory itself looks fine. And on a mount, the filesystem's own options win: a share mounted with fixed ownership, or a drive whose mount options force a uid, will refuse writes to ollama regardless of what chown appears to report.
How Do You Read journalctl When It Will Not Start
systemctl status ollama gives you the verdict; journalctl -u ollama gives you the reason. Start with status, then go to the journal for the line above the failure.
systemctl status ollama
journalctl -u ollama --no-pager -n 100
The lines that matter, and what each one is telling you:
| Journal line | Meaning |
|---|---|
Main process exited, code=exited, status=1/FAILURE | Ollama started, hit a fatal error and exited on its own. The real error is the line immediately above this one |
Failed with result 'exit-code' | systemd's summary of the above. Carries no extra information |
Scheduled restart job, restart counter is at N | Restart=always is retrying. A counter climbing every three seconds means a configuration problem, not a transient one |
Error: listen tcp 127.0.0.1:11434: bind: address already in use | Something else holds the port — usually a manually started ollama serve, or a second copy installed by a different method |
Error: mkdir <path>: permission denied | The ollama user cannot write where you pointed it. Previous section |
Failed with result 'oom-kill' | The kernel killed it for memory. Issue #11231 records this on NixOS |
ollama.service: Failed to determine user credentials | The ollama user does not exist. Reinstall, or create it with the useradd line above |
The single most useful habit here is to read upward. systemd's own messages are consequences; Ollama's error is the cause, and it is printed first. People paste the systemd lines into a search box and find nothing, because those lines are generic to every failing service on Linux.
For a start failure you cannot decode, take the daemon out of systemd for one run:
sudo systemctl stop ollama
sudo -u ollama OLLAMA_DEBUG=1 /usr/local/bin/ollama serve
That runs the same binary, as the same user, with the same restrictions, but with the output on your screen and debug logging on. If it works there and fails under systemd, the difference is environment or ordering, and systemctl cat will show you what.
Why Does the GPU Work From the CLI but Not the Service
Because the service runs as ollama, not as you, and GPU access is a group membership. The classic report is "ollama serve in my terminal uses the GPU, the systemd service falls back to CPU" — same binary, same models, same machine.
The install script anticipates this by adding the service account to the render and video groups when those groups exist, which is what grants access to the DRM device nodes on most distributions. It runs those usermod commands only if the groups are present at install time, so on a machine where drivers were installed after Ollama, the membership may simply be absent. Check it:
id ollama
ls -l /dev/dri
If ollama is not in the group that owns those device nodes, add it and restart:
sudo usermod -a -G render,video ollama
sudo systemctl restart ollama
Related reports worth knowing about, both closed: #14874 ("GPU is not used, while using as system service (systemd)") and #12708 ("failure during GPU discovery (AMD 7900 XT) using systemd, but OK from command line"). The shape is identical in both: works interactively, fails as a service. #13535 is the same family from the other direction — a ROCm user unable to override OLLAMA_LIBRARY_PATH for the service.
Two other environment-shaped causes to rule out before blaming drivers. The unit's frozen Environment="PATH=$PATH" may not contain the directory holding your GPU runtime, and LD_LIBRARY_PATH is not set in the unit at all, so a library path that works in your interactive shell is absent for the daemon. Both are fixable with additional Environment= lines in the same drop-in. If the GPU is not being used at all and none of this applies, the cause is more likely to be driver or model related, and our Ollama troubleshooting guide covers that side.
Should You Run Ollama as Your Own User
Sometimes yes — and there are two ways to do it, with different tradeoffs.
The reason to consider it is that most of the friction on this page comes from the split between your account and ollama: model directories in your home, GPU group membership, files you want to read directly. Running the daemon as yourself deletes that entire category of problem.
Option one, change the user in a drop-in:
[Service]
User=yourname
Group=yourname
Environment="OLLAMA_MODELS=/home/yourname/.ollama/models"
Set OLLAMA_MODELS at the same time, because the default path is derived from the ollama user's home and will not be writable by your account. Then daemon-reload and restart. The cost is a straightforward security tradeoff: a network-facing daemon with no authentication now runs with your file permissions instead of a locked-down system account with no shell. On a single-user workstation that is a defensible choice; on anything shared it is not. If the machine is reachable by anyone else, read our securing Ollama guide before you make this change.
Option two, run it as a systemd user service — a unit under ~/.config/systemd/user/ managed with systemctl --user, which starts with your session rather than at boot. Issue #17433 ("allow to run as a systemd user service", closed) tracked support for this. Note the behavioural difference: a user service does not run before you log in and stops when your session ends unless lingering is enabled, so it is the wrong choice for a headless box you SSH into and the right one for a desktop.
Either way, systemctl cat ollama remains the way to confirm what is actually in effect, and everything in this page about drop-ins applies unchanged.
Why Does an Upgrade Undo Your Changes
Because the install script rewrites /etc/systemd/system/ollama.service and re-runs systemctl enable on every run. If you edited the unit file itself, your changes are gone after the next curl -fsSL https://ollama.com/install.sh | sh.
This is what #13178, #14647, #8389 and #8048 are all about, and why #10390 ("make systemd service conditional, only create if it doesn't exist") is still open.
A drop-in in /etc/systemd/system/ollama.service.d/override.conf is a different file and is not touched by the installer. That is the entire reason to prefer systemctl edit over editing the unit, and it is why the --full flag — which replaces the main unit file instead of creating a snippet — is the wrong tool for this service specifically.
After any upgrade, the two-command sanity check:
systemctl cat ollama
journalctl -u ollama --no-pager | grep "server config"
If the drop-in is still listed and your values are still in the config line, the upgrade left you alone.
FAQ
Why doesn't export OLLAMA_HOST work on Linux?
Because the daemon is started by systemd as the ollama user and inherits systemd's environment, not your shell's. Exporting a variable only affects processes your shell launches. Put it in a drop-in with sudo systemctl edit ollama under a [Service] header, run daemon-reload, restart, and confirm with journalctl -u ollama --no-pager | grep "server config".
Where is the Ollama systemd service file?
The official install script writes it to /etc/systemd/system/ollama.service. Your overrides should go in /etc/systemd/system/ollama.service.d/override.conf instead, because the installer rewrites the unit file on every run. systemctl cat ollama prints both, in the order systemd merges them.
What is the correct Environment= syntax for ollama.service?
One assignment per line, with the quotes around the whole thing: Environment="OLLAMA_HOST=0.0.0.0:11434", placed under a [Service] header. No shell expansion happens, so ~, $VAR substitution and globs are all literal text. Follow the edit with sudo systemctl daemon-reload and sudo systemctl restart ollama.
How do I see the environment the Ollama daemon is actually using?
systemctl show ollama --property=Environment shows what systemd will apply, and sudo tr '\0' '\n' < /proc/$(pgrep -x ollama)/environ shows the live process. The most useful is Ollama's own startup line: journalctl -u ollama --no-pager | grep "server config", which prints every setting the server resolved, including defaults you never set.
Why does ollama.service fail to start after I set OLLAMA_MODELS?
Because the ollama user cannot write to the path. The journal shows Error: mkdir <path>: permission denied followed by status=1/FAILURE. Check the whole path, not just the target directory — in issue #9335 the models directory was 777 and owned correctly, but a parent directory denied traversal. namei -l <path> shows every component's mode at once.
Why does systemctl mask ollama fail with "File already exists"?
Because masking works by creating a symlink at /etc/systemd/system/ollama.service, and the installer has already written a real file at that exact path. Issue #17345 documents it and PR #17363 proposed moving the unit to the vendor directory. Until that lands, systemctl disable --now ollama is the practical alternative, with the caveat that a reinstall re-enables it.
Does this apply to macOS or Windows?
No. There is no systemd on either. On macOS the desktop app is the server and variables are set with launchctl setenv followed by an app restart; on Windows you quit from the tray, edit your account's environment variables, and start it again. The complete Ollama guide covers the per-platform install and configuration paths.
Sources
- ollama/ollama — scripts/install.sh (unit contents, install path,
useraddand group commands, read onmain, 23 August 2026) - Ollama FAQ (the
systemctl edit ollama.serviceprocedure, the default Linux model path, and theollamauser's access requirement) - Ollama troubleshooting docs (the
journalctlinvocation andOLLAMA_DEBUG) - systemctl(1) and systemd.unit(5) (drop-in paths, merge order,
mask,cat,daemon-reload) - ollama/ollama issues #17345, #9335, #13178, #14647, #14874, #12708, #11231, #17433, and PRs #17363, #14895
Ollama’s running. Here’s what to build with it.
Go from “ollama run” to RAG apps, agents, and fine-tuned models — structured and hands-on. First chapter free.
Stop piecing Ollama together from blog posts
Ollama Mastery is 15 chapters end to end — install, model choice, Modelfiles, GPU offload, the API, and the 20 errors that actually happen. Plus 24 more courses.
Liked this? 20 full AI courses are waiting.
From fundamentals to RAG, agents, MCP servers, voice AI, and production deployment with real GitHub repos. First chapter free, every course.
Build Real AI on Your Machine
RAG, agents, NLP, vision, and MLOps - chapters across 25 courses that take you from reading about AI to building AI.
Want structured AI education?
25 courses, 519+ chapters, from $9. Understand AI, don't just use it.
Continue Your Local AI Journey
- PILLARBest Ollama Models 2026: 15 Ranked (Coding, Reasoning, Chat)
- AI on QNAP & TrueNAS: Run Ollama with GPU Passthrough
- AI on Steam Deck: Run Local LLMs with Ollama on SteamOS
- Air-Gapped AI Deployment: Install Ollama With No Internet
- Best Free AI Models to Run Locally With Ollama, No API Key
- Best Local LLMs for Tool & Function Calling (2026 Tested)
- Best Ollama Embedding Models: We Benchmarked All 6 for RAG
- Best Ollama Models for 8GB RAM 2026: 12 Tested Local Picks
- Best Ollama Models for AI Agents 2026: 9 Tested & Ranked
- Best Uncensored Local LLMs: Abliterated Ollama Models
Comments (0)
No comments yet. Be the first to share your thoughts!