Docker vs systemd on a GB10: Reboots, Logs and GPU Memory
Everyone has a strong opinion about containers until the machine reboots at 2am and the model never comes back. On a single-GPU GB10 there is no second card, no scheduler, and nobody on call. The real difference between Docker and a systemd unit comes down to four things: update friction, GPU memory isolation, log capture, and what happens on reboot.
Two ways to run the same process
vLLM is a Python process that reads weights onto the GPU and serves an OpenAI-compatible API on a port. Both options run that exact process. The difference is who owns it.
With systemd, the process is a child of PID 1. Python, CUDA and the driver all come from the host. Everything about the launch lives in one unit file you can read in ten seconds.
[Unit]
Description=vLLM server
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=llm
Environment=HF_HOME=/srv/models/hf
ExecStart=/opt/vllm/bin/vllm serve /srv/models/model-a \
--served-model-name local \
--host 0.0.0.0 --port 8000 \
--gpu-memory-utilization 0.85 \
--max-model-len 16384
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=vllm
[Install]
WantedBy=multi-user.target
No systemd service type knows when the weights finished loading. systemctl start returns long before /health answers, so treat the unit as “process launched”, not “model ready” and put the readiness check somewhere else.
With Docker, dockerd owns the process. The image carries its own CUDA userspace, and nvidia-container-toolkit injects the driver at start. Two things bite everyone once: the Hugging Face cache must be a volume or every restart re-downloads tens of gigabytes of weights, and the default /dev/shm is too small for vLLM, which is why ipc: host appears in every serious compose file.
services:
vllm:
image: vllm/vllm-openai:latest # pin a digest for anything you rely on
runtime: nvidia
ipc: host
environment:
- HF_HOME=/models/hf
volumes:
- /srv/models:/models
ports:
- "8000:8000"
command: >
--model /models/model-a
--served-model-name local
--gpu-memory-utilization 0.85
--max-model-len 16384
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
interval: 30s
start_period: 600s
retries: 3
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
Update friction and rollback
The systemd path is fast and slightly sloppy. Keep two virtualenvs and swap a symlink:
python3 -m venv /opt/vllm-next
/opt/vllm-next/bin/pip install "vllm==0.x.y"
ln -sfn /opt/vllm-next /opt/vllm # the unit runs /opt/vllm/bin/vllm
systemctl restart vllm
Rollback is ln -sfn /opt/vllm-prev /opt/vllm && systemctl restart vllm. The cost: nothing records what is installed, torch wheels and the host driver can drift apart on the next system upgrade, and the venv quietly becomes a snowflake. This is the same problem as any other layer of a stack that upgrades itself in place โ see Upgrade One Layer of Your AI Stack Without Breaking the Rest.
The Docker path is slower to iterate and far easier to audit. docker compose pull && docker compose up -d, rollback by changing the tag back. The image is a frozen artifact, so reinstalling the OS or moving hardware costs you a pull. The cost here: you inherit upstream’s entrypoint, Python version and CUDA base image, and on ARM64 not every tag has a build. The fallback is compiling vLLM from source, which is a long build and a fight with the CUDA base image.
Rule of thumb: weekly tinkering favours systemd, quarterly updates favour containers.
GPU memory: containers isolate nothing
A container gets the whole GPU. Without MIG partitions or separate devices, two processes on one card share the same memory pool, and no compose setting changes that. On GB10 the memory is unified โ CPU and GPU draw from the same pool โ so a mem_limit in compose is not a VRAM budget. Do not treat it as one.
The levers that actually bound usage are vLLM flags: --gpu-memory-utilization, --max-model-len, --max-num-seqs, and running one model per box. The classic failure is two services each set to 0.6 utilization. That is 1.2 of a card you do not have; the second one dies during load or gets killed later. Exit code 137 in journalctl -u vllm or in docker inspect is the tell.
Logs and what happens on reboot
journald gives you journalctl -u vllm -f and journalctl -u vllm --since "1 hour ago", with one place to search every service on the box. The default rate limiter can silently drop lines from a chatty server:
sudo tee /etc/systemd/journald.conf.d/vllm.conf >/dev/null <<'EOF'
[Journal]
RateLimitIntervalSec=0
SystemMaxUse=2G
EOF
sudo systemctl restart systemd-journald
Docker’s default json-file driver has no size cap at all. A long-running server with request logging on can fill the disk and take the whole machine down. Set max-size and max-file as in the compose file above, or change the daemon default.
On reboot, the systemd unit you enabled comes back at multi-user.target with ordering you control. Restart=always beats Restart=on-failure here, because a process that exits 0 after a crash inside the serving loop would otherwise stay dead. With Docker, restart: unless-stopped only works if dockerd is enabled too, and systemctl restart docker bounces every container including the model โ a routine daemon upgrade costs you a full weight reload. An unhealthy healthcheck does not restart a container on its own; Docker just reports the status. That is a watchdog’s job, and a watchdog that restarts too eagerly is its own outage, which is the subject of Tuning a Watchdog So It Does Not Flap.
| Concern | systemd unit | Docker container |
|---|---|---|
| Who supervises the process | PID 1, directly | dockerd, which systemd supervises |
| Update | new venv plus symlink swap | pull a new tag or digest |
| Rollback | re-point the symlink | previous tag |
| GPU memory isolation | none beyond the process | none beyond the process |
| Logs | journald, filtered by unit | json-file, needs rotation |
| Boot | systemctl enable, ordered targets | restart: policy, depends on dockerd |
Blast radius of systemctl restart docker | not applicable | restarts every container |
| Reproducibility | venv state drifts over time | image is a pinned artifact |
When each is the right answer
Choose systemd when the box has one job, when you want the shortest path from editing flags to a running server, and when boot ordering matters โ model disk mounted, network up, then serve. It also keeps every log in journald, which matters when the machine is also your workstation. The design notes in One Week of Watching a Home DGX LLM Server all assume this shape for good reason.
Choose Docker when you want the running artifact pinned, when a compose file doubles as your inventory of services on the box, when a reinstall or hardware move is likely, or when you want to test a new vLLM version next to the one that works.
There is a hybrid worth knowing: run the container from a systemd unit so PID 1 owns the lifecycle. You get the pinned image without inheriting dockerd restarts that silently change the model server’s state. It is a few lines and it removes the worst Docker failure mode on a single-purpose box.
What to do next
- Write down what “up” means before you pick a supervisor:
curl -fsS http://localhost:8000/healthreturning 200 within some deadline you choose. Everything else is decoration. - Cap the two things that can eat the box:
--gpu-memory-utilizationin your launch flags and a log size limit in journald or thejson-filedriver. - Do a real reboot test. Run
systemctl rebootand watch how long until/healthanswers. Do not estimate it โ you will be wrong, and the number changes what you build. - Decide what restarts an unhealthy-but-running server. Neither systemd nor Docker does this for you by default.
- If you want the systemd side already worked out on a GB10 โ unit files, memory planning for unified memory, health checks, a watchdog and a troubleshooting playbook โ the DGX Spark LLM Deployment Kit is a $79 one-time download at https://slashmaster6.gumroad.com/l/bppdqp.
Get DGX Spark LLM Deployment Kit
DGX Spark LLM Deployment Kit โ $79, one-time payment, instant download. See the full breakdown on the review page.
Related reading
- The Ultimate Guide to AI Automation 2026
- How I Built a 10-Product Digital Business
- Building an AI Content Factory
๐ Want to try before you buy?
Both samples are 100% free. Enter your email to unlock the download links and get our 3-day AI toolkit drip in your inbox.
No spam. Unsubscribe anytime. We respect your privacy.
Or browse the full catalogue in the Slashman Tools storefront.
Published by slashman413 โ writing practical, evergreen guides on money, productivity, developer tooling and the web. More about this site โ
Frequently Asked Questions
Can I run vLLM in Docker and still use systemd?
Yes. Point a systemd unit at the container instead of the Python process, so PID 1 owns the lifecycle. That way systemctl restart docker and daemon upgrades do not silently change the state of your model server.
Does a container stop vLLM from using too much GPU memory?
No. A container sees the whole GPU, and on GB10 the GPU shares memory with the CPU, so a compose memory limit is not a VRAM budget. Bound usage with vLLM flags like --gpu-memory-utilization, --max-model-len and --max-num-seqs.
Why does my model server not come back after a reboot?
Usually one of three things: the systemd unit was never enabled, Restart=on-failure skipped a clean exit, or with Docker the container starts before the model disk is mounted or dockerd is fully up. Check journalctl -u vllm or docker inspect for the exit code first.
๐ Recommended Tools
๐ Related Articles
- What Is Context Engineering? The New Skill After Prompt EngineeringContext engineering explained: giving AI models the right context at the right time. Techniques, examples, and โฆ
- Prompt Engineering Guide for Beginners (2026): Frameworks, Examples, and MistakesThe complete prompt engineering guide for beginners: frameworks, real examples, common mistakes, and โฆ
- AI Agents for Beginners: What They Are, How They Work, and How to Build Your First OneAI agents explained for beginners: definitions, how they differ from chatbots, real use cases, and a โฆ
- Local LLM Deployment Guide: Running AI Models on DGX Spark in 2026A complete guide to local LLM deployment in 2026. Learn how to securely run open-source AI models using the โฆ
๐ฌ Free Weekly AI Product Guides
New tools, templates and automation walkthroughs โ plus hands-on updates on the Slashman Tools catalogue. One email a week, zero fluff.
Free forever ยท No spam ยท Unsubscribe anytime ยท Sent instantly