panic: $HOME is not defined from an Ollama command inside a systemd service
means the command tried to find the models directory through the HOME variable, and
systemd starts services with no HOME at all. Set it in the unit with
Environment="HOME=/usr/share/ollama" and the panic goes away.
The runtime's own ollama.service already sets HOME, which is why the server
runs fine. The panic appears when you write a second unit that calls the
ollama client, a warm-up script, a health check, a timer, and forget that
your unit does not inherit anything from the first one.
The error
ollama-warm.sh[1197]: nomic-embed-text:latest 0a109f422b47 323 MB 100% GPU 2048 24 hours from now
ollama-warm.sh[1197]: panic: $HOME is not defined
systemd[1]: ollama-warm.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
systemd[1]: ollama-warm.service: Failed with result 'exit-code'.
Why it happens
The ollama client resolves its model store under the user's home directory
unless OLLAMA_MODELS says otherwise, and the code path that does so panics rather than
guesses when HOME is unset. Interactive shells always have HOME, so the command works when
you type it. systemd does not set HOME for a service unless the unit asks for it, so the
same command run by a timer panics. Note the order in the log above: the warm-up's HTTP
calls to the server had already loaded both models; only the final ollama ps
for the log line failed. The unit was reported as failed while having done its job, which
is the confusing part.
The fix
[Service]
Type=oneshot
# The ollama client resolves the model store from HOME; systemd sets none.
Environment="HOME=/usr/share/ollama"
ExecStart=/usr/local/bin/ollama-warm.sh
Use the runtime's own home, the one its installer created for the ollama
system user, so both processes agree on where the models are. The alternative is to set
OLLAMA_MODELS explicitly in both units; either works, but one line is enough. After
editing, systemctl daemon-reload and start the unit once by hand to see it
exit clean.
What the AI got wrong: it wrote the timer unit from the pattern of the runtime's unit without copying the environment lines, and trusted "enabled, active" from the timer as success. The journal said otherwise. A oneshot that ends in a panic after doing its work is the kind of failure a green status hides.
Where it bit us
Season four, Part 11: running it for real, installing the warm-up timer on the Ubuntu server. The models were loaded, the timer was scheduled, and the unit was red. One environment line and a daemon-reload later it was green, and the runbook carries the comment so the next unit does not repeat it.
Frequently asked
- Why does ollama work in my shell but panic under systemd?
- Your shell has HOME set; a systemd service does not unless its unit sets it. The ollama client reads HOME to find the model store and panics when it is missing. Add Environment=HOME=/usr/share/ollama to the unit.
- Which HOME should a systemd unit use for the ollama client?
- The runtime's own home, /usr/share/ollama, created by the installer for the ollama system user. That keeps the client and the server looking at the same models directory. Setting OLLAMA_MODELS in both units is the equivalent alternative.
- Did the warm-up actually fail when the unit showed exit-code?
- No. The HTTP requests that load the models had already succeeded; only the final ollama ps for the log panicked. Read the journal lines above the panic before assuming the job did nothing.
More decoded errors in the Fixes category, or start the season that produced this one at Part 1.