This directory contains configuration files for deploying AI Runner headless server as a Linux system service.
The packaged airunner-headless.service file is a relocatable template. Use
deployment/systemd/install.sh to render it with the actual bundle root,
Python executable, and runtime data directories for the current install.
This surface is primarily for the distributed daemon install mode. The
normal entry point is:
./deployment/install_distributed.sh --role daemon --systemdThat command creates the daemon venv first, then calls this renderer with the
resolved install root and Python path. Invoke deployment/systemd/install.sh
directly only when you are manually rendering the service for a bundle or a
custom venv layout.
Linux desktop bundles and distributed daemon installs are expected to keep the AIRunner install root separate from the writable runtime data root.
- Install root: the installed application directory, for example
~/.local/airunner,~/.local/airunner/distributed/daemon, or/opt/airunner - Install Python: one of
<install>/venv/bin/python,<install>/.venv/bin/python, or<install>/bin/python - Runtime data root:
~/.local/share/airunnerunlessAIRUNNER_DATA_DIRoverrides it - Runtime configs:
<data>/runtime/configs - Runtime logs:
<data>/runtime/logs - Runtime sockets:
<data>/runtime/sockets - Runtime cache:
<data>/cache - Models:
<data>/models
The desktop launchers created by the top-level installer export
AIRUNNER_BUNDLE_ROOT, AIRUNNER_PYTHON, and the standardized runtime
directory variables so the daemon and sidecars can discover the bundle and
runtime roots predictably after relocation.
The distributed daemon installer passes the same resolved install root and
Python path through AIRUNNER_INSTALL_ROOT, AIRUNNER_TEMPLATE_ROOT, and
AIRUNNER_PYTHON before this renderer writes the systemd unit.
The rendered airunner-headless.service file allows AI Runner to run as a
background service that starts automatically at system boot.
-
Render and install the service template:
sudo bash deployment/systemd/install.sh
-
Optional overrides before rendering:
AIRUNNER_BUNDLE_ROOT=/path/to/bundleAIRUNNER_PYTHON=/path/to/pythonAIRUNNER_DATA_DIR=/path/to/runtime-data
-
Reload systemd to recognize the new service:
sudo systemctl daemon-reload
-
Enable the service to start at boot:
sudo systemctl enable airunner-headless -
Start the service:
sudo systemctl start airunner-headless
Check service status:
sudo systemctl status airunner-headlessView service logs:
# View recent logs
sudo journalctl -u airunner-headless -n 100
# Follow logs in real-time
sudo journalctl -u airunner-headless -f
# View logs since last boot
sudo journalctl -u airunner-headless -bStop the service:
sudo systemctl stop airunner-headlessRestart the service:
sudo systemctl restart airunner-headlessDisable auto-start at boot:
sudo systemctl disable airunner-headlessReload service after editing config:
sudo systemctl daemon-reload
sudo systemctl restart airunner-headlessService fails to start:
- Check logs:
sudo journalctl -u airunner-headless -n 100 - Verify the rendered Python path exists under your bundle root
- Verify the runtime data directory exists and has correct permissions
- Test manually from the bundle root with the rendered Python path
Permission issues:
# Ensure the service user can access the runtime data directory
sudo chown -R airunner:airunner ~/.local/share/airunner
# Check if user can write to runtime logs
ls -la ~/.local/share/airunner/runtime/Service won't stop:
# Force stop
sudo systemctl kill airunner-headless
# Check for lingering processes
ps aux | grep airunnerThe service is configured with:
- Auto-restart: Service will automatically restart if it crashes
- Restart delay: 10 seconds between restart attempts
- File limits: Increased to 65536 for handling many connections
- Local-only bind defaults: The packaged unit binds the daemon to
127.0.0.1 - Runtime directories: Runtime config, logs, sockets, cache, and model roots live under
~/.local/share/airunner - Sandboxing: The service uses
NoNewPrivileges,PrivateTmp,ProtectSystem=full,ProtectHome=read-only, and a restricted writable path - Logging: By default the daemon logs to stdout/stderr only, so systemd captures everything in
journalctl; runtime log files under~/.local/share/airunner/runtime/logsare only written whenAIRUNNER_SAVE_LOG_TO_FILE=1
The service sets these environment variables:
AIRUNNER_HEADLESS=1- Run in headless mode (no GUI)AIRUNNER_LLM_ON=1- Enable LLM serviceAIRUNNER_RUNTIME_BIND_HOST=127.0.0.1- Keep managed runtimes on loopback by defaultAIRUNNER_BUNDLE_ROOT- Resolved install root for the rendered bundleAIRUNNER_PYTHON- Resolved bundle Python executableAIRUNNER_DAEMON_CONFIG- Standard daemon config pathPATH- Prefers the rendered bundle'sbindirectory
The standardized runtime layout is:
~/.local/share/airunner/runtime/configsfor daemon and sidecar config files~/.local/share/airunner/runtime/logsfor daemon and sidecar logs~/.local/share/airunner/runtime/socketsfor local socket-style discovery paths~/.local/share/airunner/cachefor runtime-owned caches~/.local/share/airunner/modelsfor default model storage
To add more environment variables, edit the service file and add lines like:
Environment="YOUR_VAR=value"Once the service is running, test it:
# Health check
curl http://localhost:8080/health
# Test LLM endpoint
curl -X POST http://localhost:8080/llm \
-H "Content-Type: application/json" \
-d '{
"prompt": "Hello",
"action": "CHAT",
"stream": false,
"llm_request": {
"max_new_tokens": 50
}
}'Once the service is running, you can use the BookSite classification:
cd /path/to/airunner/booksite
./manage.py ai_process_books --books 535The Django app will automatically connect to http://localhost:8080 and use the RAG tools.
To remove the service:
# Stop and disable
sudo systemctl stop airunner-headless
sudo systemctl disable airunner-headless
# Remove service file
sudo rm /etc/systemd/system/airunner-headless.service
# Reload systemd
sudo systemctl daemon-reload