dsh headless auf VPS mit systemd ausführen
Betreiben Sie dsh headless auf einem VPS mit eigenem Benutzer, fester Version, Restart-Regeln und journalctl. Zugriff auf die UI per SSH-Tunnel.
dsh ohne Terminal auf einem VPS ausführen
dsh ohne Terminal auf einem VPS auszuführen, erfordert eine systemd-Unit-Datei und einen eigenen Benutzer, dem die Unit gehört. dsh ist der Befehlszeilenstarter für DeepSeek Harness, die Agent-Laufzeitumgebung von DeepSeek. Sie wurde im August 2026 unter der MIT-Lizenz als Entwicklervorschau veröffentlicht. Der Quickstart fordert Sie zur Eingabe von npx @deepseek-ai/dsh web auf. Das ist korrekt, aber der Prozess wird beendet, sobald Sie Ihre SSH-Sitzung (Secure Shell) schließen.
Eine Unit-Datei behebt vier Probleme gleichzeitig. Der Dienst wird nach einem Reboot wieder gestartet. Seine Ausgabe wird in das Journal geschrieben, statt an Ihnen vorbeizuscrollen. Er läuft unter einem Benutzerkonto, das nicht root ist. Außerdem wird genau die von Ihnen ausgewählte Version ausgeführt. Das ist hier besonders wichtig, weil der Upstream ausdrücklich darauf hinweist:
DeepSeek Harness befindet sich derzeit in der Entwicklervorschau und wird schnell weiterentwickelt. ES WIRD ÄNDERUNGEN GEBEN, DIE DIE KOMPATIBILITÄT BRECHEN.
Diese Anleitung setzt voraus, dass dsh bei Ihnen bereits manuell funktioniert. Falls nicht, beginnen Sie mit DeepSeek Harness auf einem VPS installieren und kehren Sie zurück, sobald npx @deepseek-ai/dsh web eine Seite ausliefert.
Zuerst Node, weil npm Sie sonst nicht warnt
node -vDas Paket von Ubuntu 24.04 enthält Node 18 (18.19.1 im August 2026). Für ein Paket, das in diesem Jahr veröffentlicht wurde, ist das veraltet. @deepseek-ai/dsh veröffentlicht kein Feld engines. Deshalb gibt npm keine Warnung EBADENGINE aus, wenn Ihre Node-Version zu alt ist. Der Fehler tritt stattdessen erst zur Laufzeit auf. Das kann ein Syntaxfehler oder ein fehlendes integriertes Modul sein. Eine solche Fehlersuche ist deutlich ungünstiger. Installieren Sie eine aktuelle Long-Term-Support-Version (LTS) von NodeSource:
curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
less /tmp/nodesource_setup.sh
sudo -E bash /tmp/nodesource_setup.sh
sudo apt install -y nodejs
node -vnode -v sollte jetzt eine v22-Version ausgeben. Die Zeile less ist enthalten, weil das direkte Weiterleiten eines entfernten Skripts an bash Code ausführt, den Sie zuvor nicht gelesen haben.
Nachweisen, dass der Dienst läuft, bevor Sie eine Unit schreiben
npx @deepseek-ai/dsh@0.1.0-rc.7 webLassen Sie den Prozess laufen. Öffnen Sie eine zweite SSH-Sitzung:
curl -fsS http://127.0.0.1:3080/ -o /dev/null && echo upup bedeutet, dass das Webprofil auf der Loopback-Schnittstelle lauscht. Dort wird es standardmäßig gebunden. curl: (7) Failed to connect to 127.0.0.1 port 3080: Connection refused bedeutet, dass dies nicht der Fall ist. Das erste Terminal zeigt Ihnen den Grund. Beenden Sie den manuellen Start mit Ctrl+C, bevor Sie fortfahren: Eine Unit, die einen bereits belegten Port binden soll, schlägt mit Error: listen EADDRINUSE: address already in use 127.0.0.1:3080 fehl.
0.1.0-rc.7 war die veröffentlichte Version am 18 August 2026. Prüfen Sie mit npm view @deepseek-ai/dsh version, welche Version aktuell ist, und legen Sie anschließend die gewünschte Version fest.
Installieren Sie die festgelegte Version global
npx ist in einer Unit-Datei das falsche Werkzeug. Beim Start des Prozesses wird damit die Paketversion aufgelöst. Dadurch kann ein Neustart in drei Monaten einen anderen Build eines Agents aus der Preview-Phase starten, ohne dass Sie etwas geändert haben. Außerdem muss das npm-Registry beim Booten erreichbar sein. Ist das Registry an diesem Tag langsam, schlägt die Unit statt auf der funktionierenden Maschine fehl. Installieren Sie das Paket einmalig mit einer dokumentierten Version:
sudo npm install -g @deepseek-ai/dsh@0.1.0-rc.7
command -v dsh
npm ls -g --depth=0 @deepseek-ai/dshcommand -v dsh gibt /usr/bin/dsh aus, wenn npm aus NodeSource stammt, und /usr/local/bin/dsh, wenn es aus dem eigenen Ubuntu-Paket stammt. Verwenden Sie den tatsächlich ausgegebenen Pfad in der Unit-Datei. npm ls -g gibt die exakte Version aus. Diese Information benötigen Sie in sechs Wochen, wenn sich das Verhalten ändert und Sie sich nicht mehr daran erinnern, was Sie installiert haben.
Ein Benutzer, dem der Dienst gehört und sonst nichts
Der Agent führt Shell-Befehle aus. Das ist seine Aufgabe. Wenn Sie ihn als root ausführen, wird jeder Tool-Aufruf mit root-Rechten ausgeführt. Geben Sie ihm daher ein eigenes Konto ohne Login-Shell.
sudo useradd --system --create-home --home-dir /var/lib/dsh --shell /usr/sbin/nologin dsh
sudo install -d -o dsh -g dsh -m 750 /var/lib/dsh/harness /var/lib/dsh/workspace
id dsh/var/lib/dsh/harness wird zu DSH_HOME, dem Verzeichnis, in dem dsh Profile speichert. Ein Profil ist ein benannter Stack aus Plugin-Bundles mit einer eigenen Patch-Ebene darüber. Die Profile web und headless erstellen sich beim ersten Start aus den mitgelieferten Vorlagen. Dabei werden Dateien geschrieben und möglicherweise Bundles abgerufen. Führen Sie diesen ersten Start daher manuell aus, damit Sie ihn überwachen können.
sudo -u dsh env HOME=/var/lib/dsh DSH_HOME=/var/lib/dsh/harness /usr/bin/dsh --profile webSetzen Sie HOME explizit, statt darauf zu vertrauen, wie sudo damit umgeht. Ob sudo HOME für einen Befehl ohne Login-Shell neu setzt, hängt von der Einstellung set_home in /etc/sudoers ab. Bei einer falschen Einstellung legt der erste Start Cache-Verzeichnisse in Ihrem Home-Verzeichnis an, die dsh gehören. Der Dienst kann seinen eigenen Zustand später nicht finden. Beenden Sie den Vorgang mit Ctrl+C, sobald die Prüfung auf curl den Wert up zurückgibt.
Die Unit-Datei
Schreiben Sie /etc/systemd/system/dsh.service:
[Unit]
Description=DeepSeek Harness (dsh) web profile
Documentation=https://github.com/deepseek-ai/deepseek-harness
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=exec
User=dsh
Group=dsh
WorkingDirectory=/var/lib/dsh/workspace
Environment=HOME=/var/lib/dsh
Environment=DSH_HOME=/var/lib/dsh/harness
ExecStart=/usr/bin/dsh --profile web
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
SyslogIdentifier=dsh
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetExecStart= verwendet den absoluten Pfad, den Sie mit command -v dsh ermittelt haben. systemd durchsucht für einen einfachen Befehlsnamen eine feste Liste von Pfaden. Diese Liste entspricht jedoch nicht der PATH Ihrer Shell. Ein absoluter Pfad verhindert daher Fehlzuordnungen.
WorkingDirectory= ist das Verzeichnis, in dem relative Pfade aufgelöst werden. Dort startet auch ein Tool-Aufruf, der ls ohne Argumente ausführt. Setzen Sie dieses Verzeichnis auf den Workspace, den Sie dem Agenten übergeben. Fehlt das Verzeichnis oder kann der Servicebenutzer es nicht betreten, schlägt die Unit mit status=200/CHDIR fehl, bevor dsh überhaupt gestartet wird.
ProtectHome=true blendet /home und /root für den Prozess aus. Das ist hier sicher, weil alles, worauf der Dienst zugreift, unter /var/lib/dsh liegt. Setzen Sie den Workspace auf einen Pfad unter /home. Andernfalls meldet der Agent, dass das Verzeichnis nicht existiert. Das ist zunächst verwirrend, bis Sie diese Zeile berücksichtigen. ProtectSystem=full sorgt dafür, dass /usr, /boot und /etc schreibgeschützt eingehängt werden. Der Dienst muss in diese Verzeichnisse nicht schreiben.
Weitergehende Einschränkungen sind verlockend, aber meist falsch. ProtectSystem=strict macht das gesamte Dateisystem schreibgeschützt, abgesehen von den Pseudo-Dateisystemen des Kernels. Daher schlägt bereits der erste Tool-Aufruf fehl, der eine Datei schreibt, und liefert EROFS: read-only file system. Wenn Sie diese Einschränkung benötigen, fügen Sie in derselben Bearbeitung ReadWritePaths=/var/lib/dsh hinzu.
Welcher Type= gehört hierher
Type=exec, weil dsh im Vordergrund bleibt und nie einen Fork ausführt. Das bringt gegenüber dem Standardverhalten eine echte Fehlermeldung. Bei Type=simple wertet systemd den Start unmittelbar nach dem Fork als erfolgreich, bevor bekannt ist, ob die Binärdatei überhaupt existiert. Daher kehrt systemctl start dsh sauber zurück, und der Fehler erscheint erst im Journal. Bei Type=exec wartet systemd, bis execve() erfolgreich beendet wurde. Ein Tippfehler in ExecStart= schlägt dadurch bei dem gerade eingegebenen Befehl fehl, sodass Sie ihn dort sehen.
Die beiden falschen Antworten hängen beide. Type=forking weist systemd an, auf das Beenden eines übergeordneten Prozesses zu warten. dsh wird jedoch nie beendet. Daher blockiert der Start, bis TimeoutStartSec abläuft, standardmäßig nach 90 Sekunden, und meldet anschließend Job for dsh.service failed because a timeout was exceeded.. Type=notify wartet auf eine READY=1-Nachricht über sd_notify. Ein Node-Prozess, der keine solche Nachricht sendet, führt daher zum gleichen Stillstand. Der vollständige Vergleich der systemd-Diensttypen behandelt die übrigen Aspekte, einschließlich der Frage, wann sich das Einrichten von notify lohnt.
Restart-Regeln, die Fehler sichtbar machen
Restart=on-failure startet den Dienst nach einem Exit mit einem Wert ungleich 0 oder nach einem fatalen Signal neu. Nach einem sauberen Exit bleibt die Unit beendet. Das ist das gewünschte Verhalten für einen Preview-Build. Falls dsh jemals mit Exit-Code 0 beendet wird, weil es eine Konfiguration eingelesen hat, die es nicht akzeptiert, stoppt die Unit und bleibt gestoppt. systemctl status dsh zeigt inactive (dead) an, wo Sie diesen Zustand sehen können. Restart=always macht aus demselben Ereignis eine Restart-Schleife, die aus der Entfernung intakt aussieht.
Die Rate-Begrenzung wird häufig weggelassen. Die Standardwerte von systemd erlauben fünf Starts innerhalb von zehn Sekunden. Mit RestartSec=5s erreichen Sie niemals fünf Starts innerhalb eines Zeitfensters von zehn Sekunden. Eine Unit, die beim Start abstürzt, wird daher endlos neu gestartet. Nur das Journal zeigt dieses Verhalten. StartLimitIntervalSec=300 zusammen mit StartLimitBurst=5 bedeutet, dass fünf Fehler innerhalb von fünf Minuten ausreichen. systemd gibt auf und setzt die Unit in failed. Dabei wird Start request repeated too quickly. protokolliert. Setzen Sie diesen Zustand mit sudo systemctl reset-failed dsh zurück, nachdem Sie die Ursache behoben haben. Beide Einstellungen gehören in [Unit], nicht in [Service]. Im falschen Abschnitt ignoriert systemd sie stillschweigend.
Starten und anschließend prüfen
sudo systemctl daemon-reload
sudo systemctl enable --now dsh
systemctl status dshenable --now erfüllt zwei Aufgaben. enable sorgt dafür, dass der Dienst nach einem Reboot wieder startet, und --now startet ihn beim aktuellen Boot. Ein einzelnes systemctl start gilt nur bis zum nächsten Reboot. Kernel-Updates machen Reboots erforderlich.
systemctl status dsh sollte Active: active (running), eine Main PID-Zeile und eine Memory:-Zeile anzeigen. Prüfen Sie anschließend, an welcher Adresse der Dienst lauscht:
sudo ss -lntp | grep 3080Erwartet wird 127.0.0.1:3080. Wenn 0.0.0.0:3080 angezeigt wird, wurde die Bind-Adresse geändert und Ihr Agent ist aus dem öffentlichen Internet erreichbar. Der Prozessname in dieser Ausgabe lautet node, nicht dsh, weil die dsh-Binärdatei ein Node-Skript ist und pgrep -x dsh deshalb nichts findet. Verwenden Sie stattdessen systemctl show -p MainPID dsh.
Führen Sie anschließend einmal einen Reboot durch. Ein Dienst, der einen Reboot noch nie überstanden hat, ist noch kein zuverlässiger Dienst.
sudo rebootStellen Sie die Verbindung wieder her und führen Sie systemctl is-active dsh aus. Der Befehl gibt active aus.
Logs mit journalctl lesen
Alles, was dsh nach stdout und stderr schreibt, landet im Journal unter dem Namen der Unit.
journalctl -u dsh -f
journalctl -u dsh -n 200 --no-pager
journalctl -u dsh --since "10 min ago" -p err-f zeigt neue Zeilen an, -n zeigt die letzten N Zeilen, -p err filtert nach Priorität. SyslogIdentifier=dsh in der Unit ist der Grund dafür, dass diese Zeilen mit dsh statt mit node gekennzeichnet sind. Das ist wichtig, wenn Sie erstmals eine nicht nach Unit gefilterte Journal-Ausgabe lesen.
Prüfen Sie, ob das Journal Reboots übersteht, bevor Sie es benötigen:
journalctl -u dsh -b -1Wenn dieser Befehl Specifying boot ID or boot offset has no effect, no persistent journal was found ausgibt, liegt das Journal unter /run, und bei jedem Reboot wird es gelöscht. Erstellen Sie das Verzeichnis und starten Sie den Daemon neu:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journaldÜber einen SSH-Tunnel auf die Benutzeroberfläche zugreifen, nicht über einen öffentlichen Port
dsh stellt die Webbenutzeroberfläche auf 127.0.0.1:3080 bereit und verweigert die Bereitstellung an jeder anderen Adresse. Fordern Sie --host 0.0.0.0 an. Der Dienst beendet sich dann mit dieser Ausgabe:
error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 insteadDas ist keine Einschränkung, die Sie umgehen sollten. Die Web-API steuert den Agenten, und der Agent führt Shell-Befehle aus. Ein erreichbarer Port stellt daher für jeden, der ihn findet, eine Shell auf Ihrem VPS bereit. Die Maintainer nennen die noch nicht implementierte Authentifizierung für Remote-Zugriffe als Grund dafür, dass der Dienst fest an das Loopback-Interface gebunden ist. Leiten Sie den Port stattdessen von Ihrem eigenen Rechner weiter:
ssh -N -L 3080:127.0.0.1:3080 you@203.0.113.10-L 3080:127.0.0.1:3080 öffnet Port 3080 auf Ihrem Laptop und leitet alle dort eingehenden Verbindungen an 127.0.0.1:3080 weiter, wie dieser Hostname auf dem VPS aufgelöst wird. -N bedeutet, dass kein entfernter Befehl ausgeführt wird. Die Sitzung hält dann nur den Tunnel offen. Lassen Sie sie laufen und öffnen Sie http://127.0.0.1:3080/ im Browser. Dort geben Sie unter Settings und anschließend Models den DeepSeek-API-Key ein und wählen das Workspace-Verzeichnis aus. Verwenden Sie /var/lib/dsh/workspace als Workspace. Das ist das Verzeichnis, dessen Besitzer der Dienstbenutzer ist. Andernfalls schlagen die Dateifunktionen des Agenten mit EACCES: permission denied fehl.
Wenn Port 3080 auf Ihrem Laptop bereits belegt ist, meldet ssh dies:
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080Wählen Sie mit ssh -N -L 3081:127.0.0.1:3080 you@203.0.113.10 einen anderen lokalen Port und öffnen Sie anschließend http://127.0.0.1:3081/ im Browser. Speichern Sie den Befehl in ~/.ssh/config auf Ihrem eigenen Rechner:
Host dsh-vps
HostName 203.0.113.10
User you
LocalForward 3080 127.0.0.1:3080Danach ist ssh -N dsh-vps der vollständige Befehl. Dieser Tunnel ist nun der einzige Zugang zu Ihrem Agenten. Deshalb schützt der SSH-Daemon ihn: Verwenden Sie ausschließlich Schlüssel, deaktivieren Sie die Kennwortauthentifizierung, und wenden Sie die übrigen Maßnahmen aus SSH auf Ihrem VPS härten mit besonderer Sorgfalt an.
Der Schlüssel gehört nicht in die Unit-Datei. Environment=-Werte werden von systemctl show dsh -p Environment ausgegeben, das jeder Benutzer auf dem System ausführen kann. Wenn ein von Ihnen installiertes Plugin einen Schlüssel in der Umgebung benötigt, legen Sie ihn in /etc/dsh.env ab, setzen Sie den Modus auf 600 und den Besitzer auf root. Referenzieren Sie die Datei mit EnvironmentFile=/etc/dsh.env. systemd liest diese Datei zum Zeitpunkt der Ausführung als root ein, und systemctl show gibt ihren Inhalt nicht aus.
Kosten für den Betrieb
Die Inferenz findet über die API von DeepSeek statt, nicht auf Ihrem VPS. Ihr Server verbraucht Ressourcen für den Node-Prozess, die von ihm bereitgestellte Benutzeroberfläche und jeden Befehl, den der Agent ausführt. Die ersten beiden Faktoren sind konstant und gering. Der dritte ist durch diese Unit-Datei nicht begrenzt.
Messen Sie die Grundlast auf Ihrem eigenen Server, statt einer Angabe von einem anderen System zu vertrauen:
systemctl show dsh -p MemoryCurrent
systemd-cgtop -1 --depth 2MemoryCurrent wird in Byte angegeben. Überwachen Sie den Wert, während der Agent arbeitet, nicht während er untätig ist.
Tool-Aufrufe sind untergeordnete Prozesse des Dienstes. Sie werden daher derselben Control Group zugeordnet und unterliegen denselben Limits. Ein Agent, der npm install oder eine Testsuite im Workspace ausführt, kann deutlich mehr Arbeitsspeicher als der Harness selbst verwenden. Auf einem VPS mit 1 GB treten an dieser Stelle Probleme auf: Der Kernel wählt einen Prozess aus und beendet ihn. journalctl -k | grep -i "out of memory" zeigt die Out of memory: Killed process-Zeile mit dem Namen des ausgewählten Prozesses. Dieser Prozess ist häufig nicht die Ursache des Problems.
Die Lösung ist ein Limit, das Sie bewusst festlegen. MemoryMax= und CPUQuota= im Abschnitt [Service] halten die Auswirkungen innerhalb der Unit. Dadurch wird ein außer Kontrolle geratener Build beendet, statt den gesamten Server lahmzulegen. Arbeitsspeicher und CPU mit systemd begrenzen behandelt die Werte und das Fehlerverhalten. Auch der belegte Speicherplatz wächst durch den Sitzungsverlauf unter DSH_HOME und durch alles, was der Agent im Workspace schreibt. Überwachen Sie du -sh /var/lib/dsh daher mit dem Tool, das Sie bereits zur Überwachung des Speicherplatzes verwenden.
Wenn Sie einen interaktiven Agenten benötigen, an den Sie eine Verbindung herstellen und von dem Sie sich wieder trennen, ist ein Dienst die falsche Form. Einen Agenten in einer persistenten tmux-Sitzung ausführen passt dafür besser. Führen Sie dsh als Unit aus, wenn der Agent dauerhaft aktiv und über einen Tunnel erreichbar sein soll.
Fehlerbilder und die angezeigten Meldungen
status=203/EXEC. systemd konnte die Datei nicht ausführen, und die Logs Failed to locate executable /usr/local/bin/dsh: No such file or directory. Der Pfad in ExecStart= stimmt nicht mit der Ausgabe von command -v dsh überein. Dies ist der Fehler, den Type=exec zum Zeitpunkt systemctl start meldet, statt ihn zu verbergen.
status=217/USER. Das Konto in User= ist nicht vorhanden. Bestätigen Sie dies mit id dsh.
status=200/CHDIR. WorkingDirectory= fehlt, oder der Dienstbenutzer kann es nicht betreten. sudo -u dsh ls /var/lib/dsh/workspace reproduziert den Fehler direkt.
Error: listen EADDRINUSE: address already in use 127.0.0.1:3080. Ein anderer Prozess verwendet den Port bereits. Meistens hat ein vorheriger npx-Lauf ihn in einem anderen Terminal offen gelassen. sudo ss -lntp | grep 3080 zeigt den Prozess an.
EACCES: permission denied gefolgt von einem Pfad. Die Besitzrechte unter /var/lib/dsh sind falsch. Normalerweise geschieht dies, wenn der erste Lauf als root oder mit dem falschen HOME erfolgt ist. sudo chown -R dsh:dsh /var/lib/dsh korrigiert die Besitzrechte.
Start request repeated too quickly. Die Unit hat das Start-Ratenlimit erreicht und wurde beendet. Der eigentliche Fehler steht in den darüberliegenden Zeilen. Führen Sie sudo systemctl reset-failed dsh aus, bevor Sie den Dienst erneut starten.
Die Unit ist active (running), aber der Browser zeigt nichts an. Führen Sie die Prüfung auf dem VPS aus: Wenn curl -fsS http://127.0.0.1:3080/ -o /dev/null && echo up dort up ausgibt, funktioniert der Dienst ordnungsgemäß und das Problem liegt in der Portweiterleitung.
Gezielt aktualisieren
Mit dem Festlegen einer Version bestimmen Sie selbst, wann ein Upgrade erfolgt. Lesen Sie zuerst die Release Notes, denn der eigene Hinweis des Upstream-Projekts auf inkompatible Änderungen ist der eigentliche Grund für diese Festlegung. Sichern Sie das Zustandsverzeichnis und wechseln Sie anschließend die Version:
sudo systemctl stop dsh
sudo tar czf /root/dsh-home-$(date +%F).tgz -C /var/lib/dsh harness
sudo npm install -g @deepseek-ai/dsh@0.1.0-rc.7
sudo systemctl start dsh
journalctl -u dsh -n 50 --no-pagerEin Rollback erfolgt genauso wie npm install -g, jedoch mit der alten Version. Zusätzlich stellen Sie das Tarball-Archiv wieder her. Das funktioniert nur, wenn Sie es zuvor erstellt haben. Eine Agent-Laufzeit in der Preview-Phase ist genau die Software, bei der ein Upgrade das Konfigurationsformat unter Umständen unbemerkt ändert.
FAQ
Warum beendet sich dsh, wenn ich meine SSH-Sitzung schließe?
Weil npx @deepseek-ai/dsh web ein Vordergrundprozess ist, der Ihrer Login-Sitzung gehört. Deshalb wird er beendet, wenn die Sitzung endet. Eine systemd-Unit gehört stattdessen zum Init-System. Deshalb läuft sie weiter, nachdem Sie die Verbindung trennen, und startet nach einem Reboot erneut. sudo systemctl enable --now dsh ist die Kombination aus zwei Schritten, die beides ermöglicht: enable für den Reboot und --now für diesen Bootvorgang.
Sollte ich für dsh Type=simple oder Type=exec verwenden?
Type=exec. dsh läuft im Vordergrund und erzeugt keine Forks. Daher funktionieren beide Varianten. Type=exec veranlasst systemd jedoch, auf den erfolgreichen Abschluss von execve() zu warten, bevor der Start als erfolgreich gilt. Ein falscher Pfad in ExecStart= schlägt dann mit systemctl start und status=203/EXEC direkt sichtbar fehl. Bei Type=simple meldet derselbe Fehler den Start als erfolgreich und bleibt im Journal verborgen. Type=forking und Type=notify sind hier beide falsch. Beide warten, bis TimeoutStartSec nach 90 Sekunden abläuft.
Wie öffne ich die dsh-Weboberfläche auf meinem Laptop?
Leiten Sie den Port über SSH weiter: ssh -N -L 3080:127.0.0.1:3080 you@your-vps. Öffnen Sie anschließend http://127.0.0.1:3080/ im Browser. Binden Sie den Dienst nicht an eine öffentliche Adresse. dsh weist --host 0.0.0.0 mit error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 instead zurück, weil die Web-API den Agenten Shell-Befehle ausführen lassen kann und keine Remote-Authentifizierung vorgeschaltet ist.
Kann ich dsh als root ausführen, um Berechtigungen einfach zu halten?
Nein. Das Harness führt Befehle aus und schreibt Dateien. Daher entsprechen die Berechtigungen des Dienstes den Berechtigungen des Agenten. Erstellen Sie mit useradd --system --shell /usr/sbin/nologin dsh ein Systemkonto, übertragen Sie den Besitz von /var/lib/dsh auf dieses Konto und fügen Sie NoNewPrivileges=true der Unit hinzu. Wenn anschließend EACCES: permission denied auftritt, wurden wahrscheinlich bei einem früheren Lauf als root Dateien angelegt, die root gehören. sudo chown -R dsh:dsh /var/lib/dsh behebt dieses Problem.
Welche dsh-Version sollte ich in der Unit festlegen?
Verwenden Sie die Version, die npm view @deepseek-ai/dsh version beim Einrichten des Dienstes meldet. Installieren Sie sie mit npm install -g @deepseek-ai/dsh@<that version> und halten Sie die Versionsnummer an einer Stelle fest, an der Sie sie wiederfinden. 0.1.0-rc.7 war am 18. August 2026 aktuell. Entscheidend ist nicht die Versionsnummer. Entscheidend ist, dass npx ohne Versionsangabe das Paket erst beim Start auflöst. Ein unbeaufsichtigter Neustart kann dadurch unbemerkt auf einen Build mit einem anderen Konfigurationsformat wechseln.