Skip to content

Systemd

Because MCP is stdio-only, kartoza-mcp itself doesn't need systemd. What does is your bridge — the process that hands long-lived MCP sessions out to the network.

Pattern: bridge + on-demand server

The bridge listens on websocket/HTTP and spawns one kartoza-mcp child process per session.

/etc/systemd/system/kartoza-mcp-bridge.service

# SPDX-FileCopyrightText: 2026 Kartoza.com <info@kartoza.com>
# SPDX-License-Identifier: MIT
[Unit]
Description=Kartoza MCP bridge (websocket → stdio)
After=network.target

[Service]
Type=simple
User=kartoza-mcp
Group=kartoza-mcp
ExecStart=/usr/local/bin/mcp-bridge \
    --listen 127.0.0.1:8765 \
    --child /usr/local/bin/spatial-cluster
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictRealtime=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
SystemCallArchitectures=native

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now kartoza-mcp-bridge.service
sudo journalctl -u kartoza-mcp-bridge.service -f

Why not a socket-activated server?

You can do it — Type=notify plus Sockets= — but the MCP child spawn pattern is a better fit for per-session isolation. The bridge gives you that for free.

Resource limits

Spatial workloads can spike. Set defensive limits in the unit:

[Service]
MemoryMax=512M
CPUQuota=200%
TasksMax=64

These apply to the bridge and its child MCP processes (via cgroup inheritance), which is what you want.