dsh web: Vì sao hiện http://127.0.0.1:3080?
dsh hiện http://127.0.0.1:3080 vì Web UI chỉ bind vào localhost. Dùng SSH tunnel để truy cập an toàn và tránh mở port 3080 ra Internet.
Ý nghĩa của dsh web: http://127.0.0.1:3080
Khi khởi động web profile DeepSeek Harness trên một VPS, chương trình in ra hai dòng rồi chờ:
dsh web: http://127.0.0.1:3080
Ready.127.0.0.1 là địa chỉ loopback. Đây là địa chỉ máy dùng để kết nối với chính nó. Một socket bind vào 127.0.0.1 chỉ nhận kết nối từ các process trên chính máy đó, không nhận từ nơi nào khác. Vì vậy, dòng này cho bạn biết đồng thời hai điều: Web UI đang listen ở đâu và máy nào được phép truy cập. Chỉ máy đang chạy dsh mới được truy cập.
Đó là lý do URL không hoạt động khi bạn dán vào browser trên laptop. 127.0.0.1 của laptop là loopback của chính laptop. Harness đang listen trên 127.0.0.1 của VPS, tức là một máy khác với loopback stack khác. Không có gì bị hỏng. Bạn cần chuyển tiếp kết nối qua SSH.
README chính thức nêu rõ giá trị mặc định: "Lệnh này khởi động Web UI, mặc định được phục vụ tại http://127.0.0.1:3080." Bind address lấy từ host plugin của webserver là @deepseek-ai/dsh-host-webserver. Key host của plugin này được tài liệu mô tả là "Host listen; hai giá trị được hỗ trợ là loopback và all-interfaces". Nếu bạn không thay đổi, giá trị mặc định là loopback. Nếu chưa quen với port, hãy xem cách port hoạt động trên Linux để hiểu mô hình địa chỉ cộng với port mà toàn bộ cơ chế này dựa vào.
Vì sao Web UI chỉ bind vào localhost
dsh là một agent harness, tức là chương trình bao quanh model: nó quản lý vòng lặp, các tool call và quyền mà những call đó sử dụng. Tab trình duyệt là giao diện điều khiển cho một process có thể chạy shell command, đọc và ghi file trong thư mục workspace bạn đã chọn, đồng thời sử dụng model API key của bạn. Bất kỳ ai load được trang đó đều có thể thực hiện tất cả thao tác này với tư cách user đang chạy dsh. Network cũng không phải cách duy nhất để truy cập các quyền đó: plugin bạn cài chạy bên trong cùng process và có cùng quyền, vì vậy kiểm tra plugin dsh trước khi cài cần được thực hiện cẩn thận như khi quyết định server sẽ listen trên địa chỉ nào.
Vì vậy, port 3080 không phải là dashboard chỉ để đọc. Load trang đó đồng nghĩa với việc cho phép thực thi command trên server.
Mở Web UI sẽ đưa bạn thẳng đến danh sách session. Không có prompt đăng nhập vì bản developer preview không cung cấp user account và cũng không có remote authentication. Trên loopback, điều này là hợp lý: hệ điều hành đóng vai trò access control, và chỉ các process cục bộ mới truy cập được. Bind cùng server đó vào 0.0.0.0 trên một VPS có public IP thì trang này sẽ trả lời toàn bộ Internet, không có lớp bảo vệ nào phía trước. Các automated scanner liên tục quét những port không phổ biến, vì vậy hãy coi port 3080 đã bị phát hiện ngay khi publish.
Không mở port 3080 trong firewall và không đặt webserverhostthành0.0.0.0trên public VPS. Kết hợp này trao quyền thực thi command trên server của bạn cho người kết nối đầu tiên.
Lập luận tương tự áp dụng cho mọi agent runtime bạn triển khai trên server. Vì vậy, chạy coding agent an toàn trên VPS luôn bắt đầu từ cùng một quy tắc: control port của agent phải được giữ private, và một thành phần mà bạn tin cậy sẽ đưa bạn đến đó.
Cách nào để mở Web UI của dsh từ laptop?
Có 3 cách phù hợp, và cả 3 cách đều giữ harness bind vào loopback.
- SSH tunnel. Không có listener mới nào trên public interface, và bạn đã có sẵn credential. Đây là cách nên dùng.
- Private overlay network, để bạn truy cập UI từ các thiết bị của mình nhưng người khác không thể nhìn thấy.
- Reverse proxy thực hiện TLS termination (transport layer security) và yêu cầu password trước khi forward bất kỳ request nào.
Điểm khác nhau là thành phần nào truyền trình duyệt của bạn đến loopback. Không cách nào trong số này được phép đưa harness ra khỏi loopback.
Truy cập bằng SSH tunnel
Chạy lệnh này trên laptop của bạn, không phải trên VPS:
ssh -N -L 3080:127.0.0.1:3080 you@your-vpsGiữ lệnh chạy, rồi mở http://127.0.0.1:3080 trong trình duyệt cục bộ. Web UI sẽ tải.
Đối số -L chứa 3 trường, được ngăn cách bằng dấu hai chấm. Trường thứ nhất là cổng cần mở trên laptop. Trường thứ hai và thứ ba là địa chỉ và cổng mà mỗi connection sẽ được forward đến. Điểm quan trọng là: 127.0.0.1 trong trường thứ hai được SSH server trên VPS phân giải sau khi traffic của bạn đã đến đó. Nó trỏ đến loopback của VPS, không phải loopback của laptop. Đây chính là địa chỉ mà dsh đã in ra, nên tunnel hoạt động dù kết nối trực tiếp bằng trình duyệt không hoạt động.
-N yêu cầu SSH không chạy remote command, nên bạn chỉ có forwarder mà không có shell. Để chạy tunnel ở background và báo lỗi rõ ràng thay vì im lặng:
ssh -N -f -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -L 3080:127.0.0.1:3080 you@your-vps-f đưa tunnel vào background sau khi authentication hoàn tất. ExitOnForwardFailure=yes quan trọng hơn bạn tưởng: nếu thiếu tùy chọn này, SSH vẫn kết nối thành công dù không thiết lập được forward. Kết quả là session vẫn hoạt động nhưng tunnel đã chết mà không có cảnh báo. ServerAliveInterval=30 gửi keepalive mỗi 30 giây để tunnel không bị ngắt do timeout NAT (network address translation) trên router ở quán cà phê và khách sạn.
Kết quả cần thấy
Trên VPS, xác nhận chính xác process nào đang listen:
ss -ltnp | grep 3080Kết quả đúng sẽ hiển thị địa chỉ loopback:
LISTEN 0 511 127.0.0.1:3080 0.0.0.0:* users:(("node",pid=1042,fd=21))Nếu cột địa chỉ local hiển thị 0.0.0.0:3080 thay vì vậy, Web UI đang chạy trên mọi interface, bao gồm cả interface public. Hãy dừng nó và sửa bind trước khi làm việc khác. Nếu ss in ra socket nhưng để trống trường users:, hãy chạy với sudo, vì tên process của socket thuộc về user khác sẽ bị ẩn nếu không có tùy chọn này.
Khi tunnel không khởi động được
SSH in thông báo này rồi thoát:
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080Lỗi này nằm trên laptop của bạn, không phải trên server. Một process cục bộ đã chiếm port 3080, thường là tunnel cũ mà bạn quên chưa dừng. Hãy chọn một port local còn trống:
ssh -N -L 3081:127.0.0.1:3080 you@your-vpsChỉ trường thứ nhất thay đổi, nên bây giờ bạn truy cập http://127.0.0.1:3081, còn harness vẫn listen trên 3080. Hai số này không cần phải giống nhau.
Nếu tunnel khởi động nhưng trình duyệt báo connection bị từ chối hoặc nhận phản hồi rỗng, traffic đã đi đến VPS nhưng không tìm thấy gì ở đầu bên kia. Hoặc dsh đã thoát, hoặc nó bind vào port khác. Kiểm tra bằng ss -ltnp | grep 3080 trên server.
Có một vấn đề khác thường gây nhầm lẫn. Một npx @deepseek-ai/dsh web chạy ở foreground sẽ dừng khi shell đóng, nên harness cũng dừng ngay khi bạn logout. Hãy khởi động nó bên trong tmux hoặc dưới dạng systemd user service. Đây cũng là vấn đề được giải quyết trong giữ coding agent chạy trên VPS. Trong lúc xử lý phần SSH, bạn nên thực hiện trước hardening SSH trên VPS, vì tunnel biến SSH login của bạn thành cánh cửa duy nhất đến agent.
Truy cập qua private overlay network
Overlay network cấp cho VPS và laptop của bạn các địa chỉ trong một private network mà chỉ thiết bị của bạn mới tham gia được. Tailscale là lựa chọn phổ biến, và lệnh serve của nó phù hợp chính xác với trường hợp này: tailscaled chạy trên VPS và kết nối chính nó với localhost:3080, nên harness vẫn bind trên loopback và bạn không cần thay đổi cách cấu hình dsh.
tailscale serve --bg localhost:3080
tailscale serve statusSau đó, bạn có thể truy cập UI bằng tên máy trong tailnet của mình, qua HTTPS, mà không cần mở cổng trên interface public. Cách này yêu cầu bật chứng chỉ HTTPS cho tailnet; nếu không, serve sẽ không có chứng chỉ để cung cấp. Để tắt lại, chạy lại lệnh với off:
tailscale serve --https=443 offDùng serve, không dùng funnel. Funnel public cùng target đó lên Internet, khiến bạn quay lại mô hình agent runtime không có xác thực trên một cổng mở. Hai lệnh này gần như giống hệt nhau nhưng có tác dụng trái ngược, vì vậy hãy đọc sự khác biệt giữa Tailscale Serve và Funnel trước khi chạy một trong hai lệnh. Tailscale như một private network trình bày phần thiết lập.
Truy cập qua reverse proxy có kiểm tra password
Đây là tùy chọn thực sự public một cổng ra Internet, nên authentication là lớp duy nhất ngăn người lạ thực thi lệnh trên server của bạn. Chọn cách này khi nhiều người cần dùng UI và việc tạo một tunnel cho từng người là không thực tế.
Harness vẫn chạy trên 127.0.0.1:3080. nginx chạy trên cùng máy, nên có thể truy cập loopback. nginx lắng nghe trên cổng 443 với certificate và password file.
server {
listen 443 ssl;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
auth_basic "dsh";
auth_basic_user_file /etc/nginx/dsh.htpasswd;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}Tạo password file rồi reload:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/dsh.htpasswd you
sudo nginx -t && sudo systemctl reload nginxnginx -t phải in syntax is ok rồi đến test is successful. Nếu file bị lỗi, thao tác reload sẽ fail và giữ nguyên running configuration. Vì vậy, hãy đọc lỗi thay vì restart một cách mù quáng.
Ba dòng proxy này không phải để trang trí. Các header Upgrade và Connection cho phép WebSocket handshake đi qua. Nếu thiếu chúng, trang vẫn tải được nhưng không bao giờ cập nhật. proxy_read_timeout 3600s thay thế giá trị mặc định 60 giây. Nếu không thay thế, một agent run dài sẽ bị ngắt giữa chừng và UI trông như bị treo. proxy_buffering off gửi model output đến browser ngay khi output xuất hiện, thay vì giữ lại đến khi response hoàn tất. Cấu hình reverse proxy nginx, giải thích từng dòng trình bày các phần còn lại. Chọn giữa nginx, Caddy và Traefik hướng dẫn cách làm tương tự với certificate tự động.
Dù chọn proxy nào, vẫn phải đóng cổng 3080 trên firewall để đường truy cập duy nhất là qua proxy đã authentication. Kiến thức cơ bản về firewall ufw trình bày các rule cần dùng. Basic authentication qua TLS chỉ là mức bảo vệ tối thiểu, chưa phải một security model hoàn chỉnh: ai có password đó thì có quyền truy cập shell trên server của bạn. Khi có thể, hãy ưu tiên tunnel.
Làm thế nào để đổi cổng mà web của dsh lắng nghe?
--port thuộc về web application, không phải launcher. Tài liệu CLI đưa ra ví dụ trực tiếp:
dsh --profile web --port 8080dsh web là alias của --profile web, nên dsh web --port 8080 là cùng một command. Launcher chỉ phân tích các flag của chính nó rồi chuyển mọi thứ phía sau cho profile đã khởi động. Vì vậy, các flag của launcher phải đứng trước; token đầu tiên mà launcher không nhận diện sẽ bắt đầu phần đối số của application. Đặt --port sau profile, không đặt trước profile.
Hãy đọc URL mà command in ra thay vì tự giả định, vì dòng đó cho biết địa chỉ mà server thực sự bind. Sau đó cập nhật field cuối của tunnel cho khớp:
ssh -N -L 3080:127.0.0.1:8080 you@your-vpsNếu muốn thay đổi cố định, hãy đặt cổng trong cấu hình của profile thay vì trên command line. Các profile web và headless tự khởi tạo khi được dùng lần đầu từ các template đi kèm, trong ~/.dsh. Đây cũng là thư mục chứa thiết lập API key và model endpoint. Vì vậy, cấu hình key, model và endpoint cho dsh là phần hướng dẫn liên quan nên đọc khi bạn đã chỉnh sửa các file này. Để xem cấu hình thực tế sau khi tất cả các lớp được hợp nhất:
dsh --dump-configwebserver plugin chỉ cung cấp đúng 2 key là host và port. Đặt port thành 0 sẽ yêu cầu hệ điều hành cấp một cổng còn trống, được tài liệu mô tả là “zero requests an OS-assigned port”. Cách này bảo đảm không xảy ra xung đột, nhưng không phù hợp với tunnel vì số cổng sẽ thay đổi sau mỗi lần restart.
Vì sao dsh báo lỗi address already in use?
Vì một tiến trình khác đã chiếm địa chỉ và cổng đó, nên kernel từ chối lần bind thứ hai. Node báo lỗi theo dạng sau:
Error: listen EADDRINUSE: address already in use 127.0.0.1:3080Tìm tiến trình đang chiếm địa chỉ trước khi thay đổi bất kỳ cấu hình nào:
sudo ss -ltnp | grep 3080Trường users:(("node",pid=1042,fd=21)) cho biết tên tiến trình và PID của tiến trình đó. Trường hợp thường gặp là một dsh cũ mà bạn tưởng đã dừng, nhưng thực tế vẫn đang chạy trong một cửa sổ tmux đã tách. Dừng tiến trình đó bằng kill 1042, hoặc khởi động instance mới trên một cổng khác. Lưu ý rằng 127.0.0.1:3080 và 0.0.0.0:3080 cũng xung đột với nhau, vì bind trên tất cả interface đã bao gồm loopback.
Ghim phiên bản vì đây là bản xem trước cho nhà phát triển
README nói rất rõ: DeepSeek Harness đang ở bản xem trước cho nhà phát triển và được cập nhật nhanh, nên sẽ có những thay đổi làm mất tính tương thích. Nếu tốc độ thay đổi này khiến bạn do dự, dsh khác Claude Code và Omnigent như thế nào sẽ so sánh công cụ này với hai harness đang ở các giai đoạn khác nhau trên cùng quỹ đạo phát triển.
npx @deepseek-ai/dsh web luôn phân giải thành phiên bản mới nhất đã phát hành mỗi khi bạn chạy nó. Một server không được bạn chạm vào trong một tuần có thể khởi động một CLI khác ở lần chạy tiếp theo, với các flag khác. Hãy ghim phiên bản để restart không trở thành một lần nâng cấp:
npx @deepseek-ai/dsh@0.1.0-rc.7 webTính đến tháng 8 năm 2026, package đã phát hành là phiên bản 0.1.0-rc.7. Hãy kiểm tra phiên bản mà một npx không ghim sẽ tải về trước khi chấp nhận:
npm view @deepseek-ai/dsh versionNếu không thể cài phiên bản đã ghim, hoặc npx vẫn khởi động bản build cũ sau khi bạn ghim bản mới, các lỗi cài đặt và phiên bản mà thao tác này gây ra sẽ hướng dẫn xóa cache của npx và kiểm tra npm đi kèm với Node.
Giữa các bản preview, các flag được chuyển qua lại giữa launcher và web application. Nếu --port không còn hoạt động như hướng dẫn này mô tả, hãy yêu cầu application cung cấp danh sách flag của chính nó thay vì đoán:
dsh --profile web --helpĐể xem phần cài đặt, thiết lập workspace và model key, hãy xem cài đặt DeepSeek Harness trên VPS. Nếu chỉ cần hướng dẫn ngắn hơn cho bước truy cập, truy cập Web UI của dsh trên VPS trình bày cách dùng tunnel mà không giải thích phần lý do.
FAQ
Vì sao tôi không thể mở http://127.0.0.1:3080 trong trình duyệt trên laptop?
Vì 127.0.0.1 là máy mà bạn đang nhập lệnh. DeepSeek Harness Web UI bind vào địa chỉ loopback của VPS, nên chỉ các process trên VPS mới kết nối được. Laptop của bạn có loopback riêng và không có process nào listening trên port 3080 ở đó. Hãy forward port qua SSH bằng ssh -N -L 3080:127.0.0.1:3080 you@your-vps, rồi mở http://127.0.0.1:3080 trên máy local. Trường ở giữa của tham số -L được phân giải ở phía server. Vì vậy nó trỏ đến harness.
Bind dsh Web UI vào 0.0.0.0 trên một VPS public có an toàn không?
Không. Web UI là giao diện điều khiển một agent có thể chạy shell command và sửa file bằng quyền của user đang chạy dsh. Bản developer preview cũng hoàn toàn không có màn hình đăng nhập. Bind vào mọi interface trên một public IP nghĩa là bất kỳ ai truy cập được port 3080 đều có thể thực thi command trên server của bạn. Hãy giữ bind trên 127.0.0.1, đóng port 3080 tại firewall, và dùng SSH tunnel, private overlay network hoặc reverse proxy có yêu cầu password.
Làm cách nào để dsh Web UI tiếp tục chạy sau khi tôi đóng phiên SSH?
Một npx @deepseek-ai/dsh web chạy ở foreground là process con của login shell. Vì vậy nó sẽ bị kill khi shell đó thoát. Hãy chạy nó trong một phiên tmux rồi detach bằng Ctrl-b d, hoặc chạy nó dưới dạng systemd user service với lingering được bật. Tunnel và harness hoạt động độc lập. Bạn có thể ngắt và tạo lại SSH tunnel bao nhiêu lần tùy ý mà không ảnh hưởng đến harness đang chạy, miễn là harness có parent process tồn tại lâu hơn login của bạn.
Vì sao dsh Web UI bị treo giữa chừng khi agent chạy lâu phía sau nginx?
Vì proxy_read_timeout mặc định của nginx là 60 giây. nginx sẽ đóng connection không trả về dữ liệu trong một phút, điều này dễ xảy ra trong một bước agent chạy lâu. Hãy đặt proxy_read_timeout 3600s; trong block location. Thêm proxy_buffering off; để output được stream đến trình duyệt ngay khi có dữ liệu, đồng thời truyền các header Upgrade và Connection bằng proxy_http_version 1.1; để WebSocket handshake thành công. Nếu thiếu các header này, trang vẫn tải được nhưng không bao giờ nhận được update.