SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-27

dsh auf VPS mit systemd ohne Terminal ausführen

dsh als systemd-Dienst auf einem VPS einrichten: eigener Benutzer, feste Version, Restart-Regeln, journalctl-Logs und SSH-Tunnel zur Weboberfläche.

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 sie gehört. dsh ist der Befehlszeilenstarter für DeepSeek Harness, die Agent-Laufzeitumgebung von DeepSeek, die im August 2026 unter der MIT-Lizenz als Entwicklervorschau veröffentlicht wurde. Ein Harness ist das Programm um das Modell herum und nicht das Modell selbst. Unter systemd legen Sie daher die Schleife, die Tools und die Berechtigungen ab, nicht die Inferenz von DeepSeek. Die Schnellstartanleitung fordert Sie auf, npx @deepseek-ai/dsh web einzugeben. Das ist korrekt. Der Prozess wird jedoch beendet, sobald Sie Ihre SSH-Sitzung (Secure Shell) schließen.

Eine Unit-Datei löst 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 läuft genau die von Ihnen ausgewählte Version. 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 ZU INKOMPATIBLEN ÄNDERUNGEN KOMMEN.

Diese Anleitung setzt voraus, dass dsh bei Ihnen bereits manuell funktioniert. Falls nicht, beginnen Sie mit der Installation von DeepSeek Harness auf einem VPS und fahren Sie fort, sobald npx @deepseek-ai/dsh web eine Seite bereitstellt.

Zuerst Node, weil npm Sie sonst nicht warnt

node -v

Das Paket von Ubuntu 24.04 enthält Node 18 (Stand August 2026: 18.19.1). Für ein in diesem Jahr veröffentlichtes Paket ist diese Version veraltet. @deepseek-ai/dsh veröffentlicht kein Feld engines. Deshalb gibt npm keine EBADENGINE-Warnung aus, wenn Ihre Node-Version zu alt ist. Der Fehler tritt stattdessen erst zur Laufzeit auf, etwa als Syntaxfehler oder wegen eines fehlenden integrierten Moduls. Das ist ein deutlich schlechterer Zeitpunkt, um ihn zu entdecken. 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 -v

node -v sollte nun eine v22-Version ausgeben. Die Zeile less ist enthalten, weil das direkte Weiterleiten eines Remote-Skripts an bash Code ausführt, den Sie nicht gelesen haben.

Nachweisen, dass der Dienst läuft, bevor Sie eine Unit schreiben

npx @deepseek-ai/dsh@0.1.0-rc.7 web

Lassen Sie den Prozess laufen. Öffnen Sie eine zweite SSH-Sitzung:

curl -fsS http://127.0.0.1:3080/ -o /dev/null && echo up

up bedeutet, dass das Webprofil auf dem Loopback-Interface 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 Lauf 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 von Ihnen verwendete Version fest.

Installieren Sie die festgelegte Version global

npx ist in einer Unit-Datei das falsche Werkzeug. Es ermittelt die Paketversion beim Start des Prozesses. Dadurch kann ein Neustart in drei Monaten einen anderen Build eines Preview-Agents starten, ohne dass Sie etwas geändert haben. Außerdem muss das npm-Registry beim Booten erreichbar sein. Ist das Registry langsam, wird aus einem funktionierenden Rechner eine fehlgeschlagene Unit. 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/dsh

command -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 an die installierte Version erinnern. Wenn die Installation fehlschlägt, command -v dsh anschließend nichts ausgibt oder die zurückgegebene Version nicht der angeforderten Version entspricht, bearbeiten Sie zunächst die üblichen Fehler bei der Installation und Versionsprüfung von dsh, bevor Sie die Unit-Datei erstellen.

Ein Benutzer, dem nur der Dienst gehört

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. Alles, was Sie später zu diesem Stack hinzufügen, wird als dieser Benutzer mit dem eigenen Datei- und Shell-Zugriff des Agents ausgeführt. Daher gehört die Prüfung eines Plugins vor der Installation zur selben Aufgabe wie das Erstellen des Kontos. Beim ersten Start werden Dateien geschrieben und möglicherweise Bundles abgerufen. Führen Sie diesen Schritt 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 web

Setzen Sie HOME ausdrücklich, statt darauf zu vertrauen, wie sudo damit umgeht. Ob sudo HOME bei einem Befehl ohne Login umschreibt, hängt von der Einstellung set_home in /etc/sudoers ab. Wenn der Wert falsch ist, 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 ihn mit Ctrl+C, sobald die Prüfung 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.target

ExecStart= 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 vermeidet daher diese Unsicherheit.

WorkingDirectory= legt fest, wie relative Pfade aufgelöst werden. Außerdem ist dies das Verzeichnis, in dem ein Tool-Aufruf ohne Argumente mit ls startet. Geben Sie hier den Arbeitsbereich an, 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 ausgeführt 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. Wenn Sie den Arbeitsbereich auf einen Pfad unter /home setzen, meldet der Agent, dass das Verzeichnis nicht existiert. Das wirkt zunächst verwirrend, bis Sie sich an diese Zeile erinnern. ProtectSystem=full stellt /usr, /boot und /etc schreibgeschützt bereit. Der Dienst muss dort nie schreiben.

Weitergehende Einschränkungen sind naheliegend, aber meistens falsch. ProtectSystem=strict macht das gesamte Dateisystem mit Ausnahme der virtuellen Kernel-Dateisysteme schreibgeschützt. Der erste Tool-Aufruf, der eine Datei schreibt, schlägt dann mit EROFS: read-only file system fehl. Wenn Sie diese Einschränkung benötigen, fügen Sie bei derselben Änderung ReadWritePaths=/var/lib/dsh hinzu.

Welches Type= gehört hierher?

Type=exec, weil dsh im Vordergrund bleibt und nie einen Fork ausführt. Das bietet gegenüber der Standardeinstellung eine echte Fehlermeldung. Bei Type=simple wertet systemd den Start als erfolgreich, sobald der Prozess einen Fork ausgeführt hat, bevor bekannt ist, ob die Binärdatei überhaupt existiert. Daher wird systemctl start dsh erfolgreich beendet, 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 führen beide zu einer Blockierung. 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 dann Job for dsh.service failed because a timeout was exceeded.. Type=notify wartet über sd_notify auf eine READY=1-Nachricht. Ein Node-Prozess, der keine solche Nachricht sendet, führt daher ebenfalls zu einer entsprechenden Blockierung. Der vollständige Vergleich der systemd-Service-Typen behandelt die übrigen Fälle, einschließlich der Frage, wann sich die Einrichtung von notify lohnt.

Restart-Regeln, die Fehler klar sichtbar machen

Restart=on-failure startet nach einem Exit mit einem Wert ungleich 0 oder nach einem fatalen Signal neu. Nach einem erfolgreichen Exit lässt es die Unit gestoppt. Dieses Verhalten ist für einen Preview-Build gewünscht. Wenn dsh jemals den Exit-Wert 0 zurückgibt, weil es eine Konfiguration eingelesen hat, die nicht gültig war, stoppt die Unit und bleibt gestoppt. systemctl status dsh zeigt dann inactive (dead) an. Dort können Sie den Zustand prüfen. Restart=always macht aus demselben Ereignis eine Neustartschleife, die aus der Entfernung gesund aussieht.

Die Rate-Limitierung wird häufig weggelassen. Die Standardwerte von systemd erlauben fünf Starts innerhalb von zehn Sekunden. Mit RestartSec=5s erreichen Sie nie fünf Starts innerhalb eines Zeitfensters von zehn Sekunden. Eine Unit, die beim Start abstürzt, wird daher dauerhaft neu gestartet. Nur das Journal zeigt diesen Vorgang an. StartLimitIntervalSec=300 mit StartLimitBurst=5 bedeutet, dass fünf Fehler innerhalb von fünf Minuten ausreichen: systemd gibt auf und setzt die Unit auf failed. Dabei wird Start request repeated too quickly. protokolliert. Setzen Sie diesen Zustand mit sudo systemctl reset-failed dsh zurück, sobald Sie die Ursache behoben haben. Beide Einstellungen gehören in [Unit], nicht in [Service]. In einem falschen Abschnitt ignoriert systemd sie stillschweigend.

Starten Sie den Dienst und prüfen Sie ihn

sudo systemctl daemon-reload
sudo systemctl enable --now dsh
systemctl status dsh

enable --now erfüllt zwei Aufgaben. enable startet den Dienst nach einem Reboot erneut, und --now startet ihn beim aktuellen Bootvorgang. Ein einfaches systemctl start gilt nur bis zum nächsten Reboot. Kernel-Updates erfordern Reboots.

systemctl status dsh sollte Active: active (running), eine Main PID und eine Memory:-Zeile anzeigen. Prüfen Sie anschließend, auf welcher Adresse der Dienst Verbindungen annimmt:

sudo ss -lntp | grep 3080

Erwartet 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 reboot

Stellen 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 auf 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 folgt neuen Zeilen, -n zeigt die letzten N Einträge an, -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 Neustarts übersteht, bevor Sie es benötigen:

journalctl -u dsh -b -1

Wenn dieser Befehl Specifying boot ID or boot offset has no effect, no persistent journal was found ausgibt, liegt das Journal in /run und wird bei jedem Neustart gelöscht. Erstellen Sie das Verzeichnis und starten Sie den Daemon neu:

sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald

Zugriff auf die Benutzeroberfläche über einen SSH-Tunnel statt über einen öffentlichen Port

dsh stellt die Benutzeroberfläche auf 127.0.0.1:3080 bereit und verweigert die Bereitstellung an jeder anderen Adresse. Wenn Sie --host 0.0.0.0 anfordern, wird Folgendes ausgegeben:

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

Das 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 ist daher eine Shell auf Ihrem VPS für jeden, der ihn findet. Die Maintainer nennen fehlende Authentifizierung für Fernzugriffe als Grund dafür, dass die Bind-Adresse fest auf das Loopback-Interface gesetzt ist. Was die Zeile 127.0.0.1:3080 in der Startausgabe tatsächlich bedeutet sollten Sie lesen, bevor Sie versuchen, diese Einstellung zu ändern. 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 Daten an 127.0.0.1:3080 weiter, wie es auf dem VPS aufgelöst wird. -N bedeutet, dass kein entfernter Befehl ausgeführt wird. Die Sitzung hält daher nur den Tunnel offen. Lassen Sie sie laufen und öffnen Sie http://127.0.0.1:3080/ in Ihrem Browser. Dort geben Sie unter Settings und anschließend Models den DeepSeek-API-Schlüssel ein und wählen das Arbeitsverzeichnis. Setzen Sie das Arbeitsverzeichnis auf /var/lib/dsh/workspace, das Verzeichnis im Besitz des Dienstbenutzers. Andernfalls schlagen die Dateifunktionen des Agenten mit EACCES: permission denied fehl.

Wenn Port 3080 auf Ihrem Laptop belegt ist, meldet ssh dies:

bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080

Wählen Sie mit ssh -N -L 3081:127.0.0.1:3080 you@203.0.113.10 einen anderen lokalen Port und rufen Sie anschließend http://127.0.0.1:3081/ im Browser auf. Speichern Sie die Eingabe in ~/.ssh/config auf Ihrem eigenen Rechner:

Host dsh-vps
  HostName 203.0.113.10
  User you
  LocalForward 3080 127.0.0.1:3080

Danach ist ssh -N dsh-vps der vollständige Befehl. Dieser Tunnel ist nun der einzige Zugang zu Ihrem Agenten. Damit schützt der SSH-Daemon den Agenten: 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 Konsequenz an. Wenn aus dem VPS ein kleines privates Netzwerk mit eigener Datenbank oder einem Staging-Host wird, erspart Ihnen Adressen mit einem Subnet-Router in Ihrem Tailnet bekannt machen eine Weiterleitung pro Dienst. Wegen der Loopback-Bindung von dsh ist die Benutzeroberfläche jedoch weiterhin nur über einen Tunnel erreichbar.

Der Schlüssel gehört nicht in die Unit-Datei. Die Werte von Environment= 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 mit den Rechten 600 an, wobei root der Eigentümer ist, und verweisen Sie mit EnvironmentFile=/etc/dsh.env darauf. systemd liest diese Datei zur Ausführungszeit als root ein, und systemctl show gibt ihren Inhalt nicht aus. In welcher Datei auf der Festplatte die einzelnen Einstellungen tatsächlich gespeichert werden und welche Daten Ihr System verlassen, wenn Sie dsh statt der DeepSeek-API auf einen lokalen Ollama-Endpunkt richten, behandelt Schlüssel, Modelle und Endpunkte in dsh konfigurieren.

Kosten für den Betrieb

Die Inferenz findet über die API von DeepSeek statt, nicht auf Ihrem VPS. Ihr Server benötigt Ressourcen für den Node-Prozess, die von ihm bereitgestellte Benutzeroberfläche und jeden Befehl, den der Agent ausführt. Die ersten beiden Posten sind konstant und gering. Der dritte wird durch diese Unit-Datei nicht begrenzt.

Messen Sie die Grundlast auf Ihrem eigenen Server, statt einer Angabe von einem fremden System zu vertrauen:

systemctl show dsh -p MemoryCurrent
systemd-cgtop -1 --depth 2

MemoryCurrent wird in Bytes angegeben. Überwachen Sie den Wert, während der Agent arbeitet, nicht im Leerlauf.

Tool-Aufrufe sind untergeordnete Prozesse des Dienstes. Sie laufen daher in derselben Control Group und werden auf dieselben Limits angerechnet. Ein Agent, der npm install oder eine Testsuite im Workspace ausführt, kann deutlich mehr Speicher als der Harness selbst verwenden. Auf einem VPS mit 1 GB führt das zum Ausfall: Der Kernel wählt einen Prozess aus und beendet ihn. journalctl -k | grep -i "out of memory" zeigt die Zeile Out of memory: Killed process an, in der der ausgewählte Prozess genannt wird. Das ist häufig nicht der Prozess, der das Problem verursacht hat.

Die Lösung ist ein bewusst gesetztes Limit. MemoryMax= und CPUQuota= im Abschnitt [Service] halten die Auswirkungen auf die Unit begrenzt. Dadurch wird ein außer Kontrolle geratener Build beendet, statt den gesamten Server lahmzulegen. Speicher und CPU mit systemd begrenzen behandelt die Werte und das Fehlerverhalten. Auch der Speicherplatz wächst durch den Sitzungsverlauf unter DSH_HOME und durch alles, was der Agent in den Workspace schreibt. Überwachen Sie deshalb du -sh /var/lib/dsh mit dem Werkzeug, das Sie bereits zur Überwachung des Speicherplatzes verwenden.

Wenn Sie einen interaktiven Agenten benötigen, an den Sie sich an- und von dem Sie sich abmelden können, ist ein Dienst nicht die passende Form. Einen Agenten in einer dauerhaften tmux-Sitzung ausführen passt dafür besser. Führen Sie dsh als Unit aus, wenn der Dienst 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, anstatt ihn zu verbergen.

status=217/USER. Das Konto in User= ist nicht vorhanden. Prüfen Sie dies mit id dsh.

status=200/CHDIR. WorkingDirectory= fehlt, oder der Dienstbenutzer kann das Verzeichnis 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. Meist wurde der npx-Prozess in einem anderen Terminal offengelassen. sudo ss -lntp | grep 3080 nennt den Prozess.

EACCES: permission denied gefolgt von einem Pfad. Die Besitzrechte unter /var/lib/dsh sind falsch. Ursache ist normalerweise, dass der erste Start als root oder mit dem falschen HOME erfolgt ist. sudo chown -R dsh:dsh /var/lib/dsh behebt das Problem.

Start request repeated too quickly. Die Unit hat das Start-Rate-Limit erreicht und den Start aufgegeben. Der eigentliche Fehler steht in den Zeilen darüber. Führen Sie sudo systemctl reset-failed dsh aus, bevor Sie es erneut versuchen.

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äß. Das Problem liegt dann in der Portweiterleitung.

Gezielt aktualisieren

Durch Version-Pinning entscheiden Sie selbst, wann ein Upgrade erfolgt. Lesen Sie zuerst die Release Notes. Die Warnung des Upstream-Projekts vor inkompatiblen Änderungen ist der eigentliche Grund für das Pinning. Sichern Sie das Zustandsverzeichnis. 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-pager

Ein Rollback erfolgt genauso npm install -g mit der alten Version. Zusätzlich stellen Sie das Tarball wieder her. Das funktioniert nur, wenn Sie zuvor ein Tarball erstellt haben. Eine Agent-Laufzeit in der Preview-Phase ist genau die Software, bei der ein Upgrade das Konfigurationsformat im laufenden Betrieb ändern kann.

FAQ

Warum stoppt dsh, wenn ich meine SSH-Sitzung schließe?

Weil npx @deepseek-ai/dsh web ein Vordergrundprozess ist, der Ihrer Anmeldesitzung gehört. Deshalb wird er beendet, sobald die Sitzung endet. Eine systemd-Unit gehört stattdessen zum Init-System. Deshalb läuft sie nach dem Trennen der Verbindung weiter 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 Boot.

Sollte ich für dsh Type=simple oder Type=exec verwenden?

Type=exec. dsh läuft im Vordergrund und erzeugt nie einen weiteren Prozess. Deshalb funktionieren beide Varianten. Bei Type=exec wartet systemd jedoch, bis execve() erfolgreich ausgeführt wurde, bevor der Start als erfolgreich gilt. Ein falscher Pfad in ExecStart= schlägt dann mit systemctl start und status=203/EXEC direkt vor Ihnen fehl. Bei Type=simple meldet derselbe Fehler Erfolg 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 Weboberfläche von dsh 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 lehnt --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 ab, weil die Web-API den Agenten Shell-Befehle ausführen lassen kann und keine Authentifizierung für entfernte Zugriffe vorgeschaltet ist.

Kann ich dsh als root ausführen, um die Berechtigungen einfach zu halten?

Nein. Das Harness führt Befehle aus und schreibt Dateien. Daher verfügt der Agent über alle Berechtigungen, die der Dienst besitzt. Erstellen Sie mit useradd --system --shell /usr/sbin/nologin dsh ein Systemkonto, übertragen Sie diesem Konto den Besitz von /var/lib/dsh und fügen Sie NoNewPrivileges=true zur Unit hinzu. Falls danach 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 notieren Sie sie an einer Stelle, 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. Dadurch kann ein unbeaufsichtigter Neustart unbemerkt auf einen Build mit einem anderen Konfigurationsformat wechseln.