SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

dsh hiện http://127.0.0.1:3080: truy cập thế nào?

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 public port 3080 ra Internet.

Ý nghĩa của dsh web: http://127.0.0.1:3080

Khi khởi động web profile của DeepSeek Harness trên VPS, chương trình in ra 2 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ó. 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 2 đ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 phép truy cập.

Đó là lý do URL không làm gì 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 mạng.

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 cung cấp tại http://127.0.0.1:3080." Bind address được 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; 2 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 port còn mới với bạn, cách port hoạt động trên Linux giải thích mô hình địa chỉ cộng port mà mọi thứ ở đây dựa trên đó.

Vì sao Web UI chỉ bind vào localhost

dsh là một agent harness. Tab trình duyệt là giao diện điều khiển cho một process chạy shell command, đọc và ghi file trong thư mục workspace bạn đã chọn, đồng thời sử dụng API key của model. Bất kỳ ai load được trang đó đều có thể thực hiện toàn bộ việc này với quyền của user đang chạy dsh.

Vì vậy, port 3080 không phải là dashboard chỉ đọc. Load trang đó sẽ cho phép thực thi command trên server.

Mở Web UI, bạn sẽ vào thẳng danh sách session. Không có prompt đăng nhập, vì bản developer preview không cung cấp user account hoặc remote authentication. Khi bind vào loopback, thiết kế này vẫn an toàn theo mô hình kiểm soát truy cập của hệ điều hành: chỉ process cục bộ mới truy cập được. Nếu bind cùng server vào 0.0.0.0 trên một VPS có public IP, trang đó sẽ trả lời toàn bộ Internet mà 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 ít phổ biến, nên hãy coi port 3080 public là đã bị phát hiện.

Không mở port 3080 trong firewall và không đặt webserver host thành 0.0.0.0 trên một public VPS. Kết hợp này trao quyền thực thi command trên server cho bất kỳ ai kết nối trước.

Lý do 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 nguyên tắc: control port của agent phải giữ private, và một thành phần 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. Cả 3 cách đều giữ harness chỉ bind vào loopback.

  • SSH tunnel. Không có tiến trình nào mới lắng nghe trên interface public, và bạn đã có sẵn thông tin xác thực. Đây là cách nên dùng.
  • Private overlay network, để UI chỉ có thể truy cập từ các thiết bị của bạn và ẩn với tất cả thiết bị khác.
  • Reverse proxy có TLS termination (transport layer security) và yêu cầu mật khẩu trước khi chuyển tiếp bất kỳ request nào.

Điểm khác nhau là thành phần vận chuyể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 chạy trên VPS:

ssh -N -L 3080:127.0.0.1:3080 you@your-vps

Để lệnh tiếp tục chạy, rồi mở http://127.0.0.1:3080 bằng trình duyệt trên máy local. Web UI sẽ tải.

Đối số -L gồm 3 trường được phân tách bằng dấu hai chấm. Trường thứ nhất là port cần mở trên laptop. Trường thứ hai và thứ ba là địa chỉ và port để chuyển tiếp từng connection đế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 VPS. 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ù connection 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, vì vậy bạn chỉ có forwarder mà không có shell. Để chạy tunnel nền 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 SSH xuống 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 connect thành công ngay cả khi không thiết lập được forward. Bạn sẽ có một session đang chạy 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 khi router ở quán cà phê hoặc khách sạn hết thời gian chờ NAT (network address translation).

Bạn sẽ thấy gì

Trên VPS, xác nhận process nào thực sự đang listen:

ss -ltnp | grep 3080

Kế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ào đó, Web UI đang listen trên mọi interface, bao gồm cả interface public. 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ì nếu không thì process name của socket do user khác sở hữu sẽ bị ẩn.

Khi tunnel không khởi động

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: 3080

Lỗi này xảy ra trên laptop của bạn, không phải trên server. Một process local đã chiếm port 3080, thường là tunnel cũ mà bạn quên tắt. Hãy chọn một port local còn trống:

ssh -N -L 3081:127.0.0.1:3080 you@your-vps

Chỉ 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 được phản hồi rỗng, traffic đã đi đến VPS nhưng không tìm thấy gì ở đầu bên kia. Có thể dsh đã thoát, hoặc process đã bind vào port khác. Kiểm tra bằng ss -ltnp | grep 3080 trên server.

Còn một vấn đề thường gây nhầm lẫn. Một npx @deepseek-ai/dsh web chạy ở foreground sẽ chết khi shell đóng, nên harness dừng ngay khi bạn logout. Hãy khởi động nó bên trong tmux hoặc dùng systemd user service. Đây cũng là cách giải quyết vấn đề tương tự như trong duy trì coding agent chạy trên VPS. Trong lúc xử lý phần SSH, bạn nên hardening SSH trên VPS trước, vì tunnel biến SSH login của bạn thành cánh cửa duy nhất để truy cập agent.

Truy cập qua private overlay network

Overlay network cung cấp cho VPS và laptop của bạn các địa chỉ trên 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à tự kết nối đến localhost:3080, nên harness vẫn chỉ listen 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 status

Sau đó, 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 public interface. 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 off

Dùng serve, không dùng funnel. Funnel public target tương tự lên Internet, khiến bạn quay lại trạng thái 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 ngược nhau, vì vậy hãy đọc sự khác nhau giữa Tailscale Serve và Funnel trước khi chạy 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 port 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. Hãy chọn cách này khi có nhiều người cần dùng UI và mỗi người chạy một tunnel riêng 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, và lắng nghe trên port 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 nginx

nginx -t phải in ra syntax is ok rồi đến test is successful. Reload với file bị lỗi 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 đó không phải để trang trí. Các header UpgradeConnection cho phép WebSocket handshake đi qua. Nếu thiếu chúng, trang vẫn tải 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, 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 dữ liệu đến, thay vì giữ lại đến khi response hoàn tất. Giải thích cấu hình reverse proxy nginx theo từng dòng trình bày phần còn lại, còn 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, hãy giữ port 3080 đóng trên firewall để đường truy cập duy nhất đi qua proxy đã được authentication. Kiến thức cơ bản về firewall ufw trình bày các rule cần dùng. Basic authentication trên TLS chỉ là mức bảo vệ tối thiểu, chưa phải security model hoàn chỉnh: ai giữ password đó thì có quyền truy cập shell trên server của bạn. Nếu có thể, hãy ưu tiên dùng 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 có ví dụ trực tiếp:

dsh --profile web --port 8080

dsh 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ứ sau đó cho profile đã boot. Vì vậy, các flag của launcher phải đặt trước, còn token đầu tiên mà launcher không nhận ra sẽ bắt đầu phần argument của application. Đặt --port sau profile, không đặt trước profile.

Hãy đọc URL mà command in ra thay vì tự đoán, vì dòng đó cho biết địa chỉ server thực sự đã bind. Sau đó cập nhật field cuối trong tunnel cho khớp:

ssh -N -L 3080:127.0.0.1:8080 you@your-vps

Nếu muốn thay đổi cố định, cổng nằm trong cấu hình của profile thay vì trên command line. Các profile webheadless tự khởi tạo trong lần sử dụng đầu tiên từ các template được đóng gói, tại ~/.dsh. Để xem cấu hình thực tế sau khi tất cả layer được ghép lại:

dsh --dump-config

Plugin webserver chỉ cung cấp đúng 2 key là hostport. Đặ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. Tài liệu ghi rõ: "zero requests an OS-assigned port". Cách này bảo đảm không xảy ra xung đột cổng, 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 lỗi “address already in use”?

Vì một process khác đã giữ địa chỉ và port đó, nên kernel từ chối thao tác bind thứ hai. Node báo lỗi theo dạng sau:

Error: listen EADDRINUSE: address already in use 127.0.0.1:3080

Hãy tìm process đang giữ địa chỉ trước khi thay đổi bất cứ thứ gì:

sudo ss -ltnp | grep 3080

Trường users:(("node",pid=1042,fd=21)) cho biết tên process và PID của nó. 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 rời. Dừng instance đó bằng kill 1042, hoặc khởi động instance mới trên một port khác. Lưu ý rằng 127.0.0.1:30800.0.0.0:3080 cũng xung đột với nhau, vì bind trên mọi interface đã bao gồm loopback.

Cố định version vì đây là bản developer preview

README nói rất rõ: DeepSeek Harness đang ở giai đoạn developer preview và được phát triển nhanh. Các thay đổi có thể làm hỏng compatibility sẽ xảy ra.

npx @deepseek-ai/dsh web luôn resolve đến version mới nhất đã publish mỗi khi bạn chạy lệnh. 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 pin version để restart không biến thành upgrade:

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

Tính đến tháng 8 năm 2026, package đã publish là version 0.1.0-rc.7. Hãy kiểm tra một lệnh npx không có version cụ thể sẽ kéo về trước khi chấp nhận:

npm view @deepseek-ai/dsh version

Các flag được chuyển qua lại giữa launcher và web application trong những bản preview khác nhau. Nếu --port không còn hoạt động như mô tả trong hướng dẫn này, hãy yêu cầu application tự hiển thị danh sách flag thay vì tự đoán:

dsh --profile web --help

Để xem phần cài đặt, thiết lập workspace và model key, đọc cài đặt DeepSeek Harness trên VPS. Nếu chỉ cần walkthrough ngắn cho bước truy cập, truy cập dsh Web UI trên VPS trình bày cách dùng tunnel mà không giải thích phần reasoning.

FAQ

Vì sao tôi không thể mở http://127.0.0.1:3080 trong trình duyệt trên laptop?

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 có thể kết nối đến đó. Laptop của bạn có loopback riêng và không có process nào listening trên port 3080 tại đó. Hãy forward port qua SSH bằng ssh -N -L 3080:127.0.0.1:3080 you@your-vps, sau đó 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 tham số này trỏ đến harness.

Có an toàn không nếu bind dsh Web UI vào 0.0.0.0 trên VPS public?

Không. Web UI là giao diện điều khiển của agent có thể chạy shell command và chỉnh sửa file với 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 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 ở 127.0.0.1, đóng port 3080 trong firewall, và dùng SSH tunnel, private overlay network hoặc reverse proxy có yêu cầu password.

Làm thế 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, nên sẽ bị kill khi shell đó thoát. Hãy chạy nó bê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 phiê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?

proxy_read_timeout mặc định của nginx là 60 giây. nginx sẽ đóng connection không tạo ra dữ liệu trong 1 phút, điều mà một agent step dài dễ gặp phải. Hãy đặt proxy_read_timeout 3600s; trong block location. Thêm proxy_buffering off; để output được stream đến browser ngay khi có dữ liệu, đồng thời truyền các header UpgradeConnection 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 load nhưng không bao giờ nhận được bản cập nhật.