Nexus Terminal: a self-hosted web SSH, RDP and VNC client built on Guacamole
一款现代化的 Web SSH / RDP / VNC 客户端,提供独立桌面端,支持人机验证、2FA、界面定制、操作审计等强大功能。
At a glance
- What is it?
- Nexus Terminal is a browser-based terminal and remote desktop client that wraps Apache Guacamole for RDP and VNC and adds SSH tab management, a Monaco-based file editor, captcha and 2FA on login, IP allow and deny lists, audit logs and session suspension. It ships as four containers on Debian amd64, needs HTTPS for clipboard use, and the ARMv7 build has RDP disabled because no guacd image exists for that architecture.
- Who is it for?
- Nexus Terminal fits an administrator or small team who wants browser-based access to many SSH hosts, occasional RDP or VNC sessions, and an audit trail, and who is willing to run four containers plus nginx on Debian amd64. It does not fit a team needing HA, a hardened public endpoint, or ARMv7 with graphical remote access.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Four containers, and which one does what
The compose file is the clearest description of the system. There are four services on a bridge network named nexus-terminal-network with IPv6 enabled and a subnet of fd01::/80.
`frontend` runs the `heavrnl/nexus-terminal-frontend:latest` image, publishes port 80 to host port 18111, and depends on both the backend and the remote gateway.
`backend` runs `heavrnl/nexus-terminal-backend:latest`, reads the `.env` file, sets `NODE_ENV=production`, `PORT=3001` and `NEXUS_DATA_DIR=/app/data`, and mounts `./data` into `/app/data`. That bind mount is the only persistent state in the stack.
`remote-gateway` runs `heavrnl/nexus-terminal-remote-gateway:latest` and is where the graphical protocols live. Its environment names the pieces: `GUACD_HOST=guacd`, `GUACD_PORT=4822`, `REMOTE_GATEWAY_API_PORT=9090`, `REMOTE_GATEWAY_WS_PORT=8080`, `FRONTEND_URL=http://frontend` and `MAIN_BACKEND_URL=http://backend:3001`.
`guacd` runs `guacamole/guacd:latest`, Apache Guacamole's proxy daemon, on port 4822. So RDP and VNC are not implemented by this project; they are delegated to Guacamole, with the remote gateway speaking to it.
That split has a direct consequence for architecture choice. The README recommends Debian on amd64 because the maintainer has no ARM device and cannot guarantee compatibility. arm64 users are told to replace `guacamole/guacd:latest` with `guacamole/guacd:1.6.0-RC1`, and on ARMv7 RDP is disabled outright because Apache Guacamole publishes no ARMv7 guacd image.
Deploying it starts with two wget calls
There is no install script; the compose file and environment file are downloaded from the repository into a directory you create:
mkdir ./nexus-terminal && cd nexus-terminalwget https://raw.githubusercontent.com/Heavrnl/nexus-terminal/refs/heads/main/docker-compose.yml -O docker-compose.yml && wget https://raw.githubusercontent.com/Heavrnl/nexus-terminal/refs/heads/main/.env -O .envThen start it:
docker compose up -dUpdates follow the same pattern, and the README notes you do not need the source tree unless you intend to build yourself:
docker compose down
docker compose pull
docker compose up -dnginx sits in front with a websocket-capable proxy block, `proxy_pass` pointed at 127.0.0.1:18111, and `proxy_set_header Upgrade`/`Connection "upgrade"` for the upgrade handshake. It also forwards `Range` and `If-Range`, which is there for partial content and streaming requests. IPv6 in Docker is optional and configured by adding an `ipv6`, `fixed-cidr-v6`, `ip6tables` and `experimental` block to `/etc/docker/daemon.json` then running `sudo systemctl restart docker`.
Session suspension is the feature SSH users care about
The suspend feature is what separates this from a plain web terminal. You right-click an SSH tab and choose to suspend the session, and the backend takes over and keeps the SSH connection alive even if the browser disconnects. You can restore it later from the panel component. The stated purpose is that compiles and long-running jobs do not get interrupted by network wobble.
That is a server-side connection pool, which is a meaningful architectural commitment and not something a purely client-side tool can do.
The command input box has its own tab navigation: when it has focus, `Alt` plus `↑/↓` switches SSH session tabs and `Alt` plus `←/→` switches text editor tabs. A command sync option, enabled in settings, types into a chosen target input source in real time, with `↑/↓` to select a menu command and `Enter` to send.
The file manager supports drag-and-drop upload from outside the browser, drag to move files inside it, multi-select with `Ctrl` or `Shift`, and a right-click menu with copy, paste, cut, delete, rename and permission changes. The README advises compressing large uploads because the browser can hang otherwise.
Text editing uses the Monaco editor, pinned at version 0.52.2 in package.json. Copy and paste in the terminal is `Ctrl + Shift + C` and `Ctrl + Shift + V`.
Security features, and the one that catches people out
Login protection is layered: hCaptcha and Google reCAPTCHA integration, two-factor authentication, and Passkey support which requires setting `RP_ID` and `RP_ORIGIN` in the `.env` file. Beyond login there are IP allow and deny lists with automatic blocking on anomalous access, a notification system covering login reminders and anomaly alerts, and audit logs that record user behavior and system changes.
The environment template describes the encryption side. `ENCRYPTION_KEY` is a 32-byte hex key for AES-256, used to encrypt sensitive database fields such as SSH passwords, private keys and passkeys. `SESSION_SECRET` signs Express sessions. Both are commented out in the template, with a note that if they are not set explicitly, the system generates strong random keys on first startup and writes them into `data/.env`.
That default is convenient and it puts key material in a file inside your data directory, so the backup of that directory becomes security-sensitive in a way the README does not spell out.
The browser limitation is the one that surprises people. Because of browser restrictions, terminal content cannot be copied on a page that is not HTTPS or localhost, so access must be over HTTPS. For a tool that holds SSH credentials and gives remote shell access, that constraint is at least forcing the right direction.
What is finished and what is not
The README is candid about two gaps. Adding two file manager components to one layout is an experimental feature that may be unstable. Adding multiple text editors to the same layout is not implemented at all.
There is also no test suite. The root `test` script in package.json is a stub:
"test": "echo \"Error: no test specified\" && exit 1"For a project holding credentials and remote access, that is worth weighing, particularly since a postinstall hook runs `patch-package`, meaning dependencies are patched on install. The overrides block in package.json pins a set of transitive versions, including `ws` at ^8.18.2 both at the top level and for `guacamole-lite`, `vite` at ^6.4.4, `multer` at ^2.1.0 and `monaco-editor` at 0.52.2.
On licensing there is a discrepancy to resolve before you build on this. The README and the LICENSE file say GPL-3.0, while package.json declares `"license": "ISC"`. The repository is also marked `private: true` with npm workspaces under `packages/*`, including a desktop package with its own build and sync scripts.
The desktop build removes the web-only features, specifically the various login verifications and session suspension, per the README.
Where Nexus Terminal is the wrong choice
Availability is the first limit. Four containers with a hard dependency on guacd, no documented high-availability mode, and a single bind mount for state means this is a tool for one admin's server, not a platform for a department. The README documents no clustering, no failover and no backup feature; it tells you to back up the data folder yourself.
Architecture support is the second. Debian amd64 is recommended because the maintainer cannot test other platforms, arm64 needs a specific guacd RC tag, and ARMv7 loses RDP entirely. If your fleet is ARM, you are outside the tested envelope.
Feature gaps are the third. Multiple text editors in one layout is not implemented, dual file managers are experimental, and the clipboard limitation means plaintext HTTP is not usable for real work.
The test and licensing situation is the fourth. No tests, patched dependencies at install time, and a GPL-3.0 versus ISC mismatch in the metadata. The GPL-3.0 reading is the stricter one and the one to plan around if you intend to distribute a modified version.
It is also worth naming what it is not. This is not a bastion host with session recording, not an identity provider, and not a compliance product. The audit log records user behavior and system changes, which is useful, but the README does not claim tamper evidence or export to a SIEM.
Against Apache Guacamole on its own, and against a desktop client
Guacamole alone is the honest baseline. It already does browser-based RDP, VNC and SSH with a client that is far more mature than anything this project adds on the graphical side, and running just `guacd` plus its web front end is two containers instead of four. What you give up is the tabbed SSH workflow, the file manager with upload and edit, the suspend feature, and the audit log.
So if your use is occasional RDP for one person, Guacamole is enough and simpler.
Against a desktop client, the comparison flips. A native SSH client plus a native RDP client is faster, more reliable, and has no server holding your credentials. Nexus Terminal earns its place when the browser is the only machine you have, when you need to reach many hosts from a locked-down endpoint, or when you need to hand access to colleagues without provisioning accounts on their laptops.
The credential-storage argument deserves weight, in both directions. Centralizing credentials in one server is convenient for a team and is exactly the thing that makes a compromise serious. The AES-256 encryption of stored secrets and the IP lists reduce that risk; they do not remove it, and the absence of tests is not reassuring on a project whose whole value is trusted access.
Editorial conclusion
Nexus Terminal fits an administrator or small team who wants browser-based access to many SSH hosts, occasional RDP or VNC sessions, and an audit trail, and who is willing to run four containers plus nginx on Debian amd64. It does not fit a team needing HA, a hardened public endpoint, or ARMv7 with graphical remote access. Before deploying, put it behind HTTPS, since clipboard copy from the terminal does not work otherwise, back up the data directory yourself because the project ships no backup feature, and set `ENCRYPTION_KEY` and `SESSION_SECRET` deliberately rather than relying on the auto-generated values written to data/.env on first run.
Frequently asked questions
How do I deploy Nexus Terminal?
Create a directory, download docker-compose.yml and .env from the repository with wget, then run `docker compose up -d`. The stack is four containers: a frontend on host port 18111, a backend on port 3001 with `./data` mounted at `/app/data`, a remote gateway, and `guacamole/guacd` for RDP and VNC. Debian on amd64 is the recommended platform.
What does the Nexus Terminal suspend session feature do?
Suspending an SSH tab from the right-click menu hands the connection to the backend, which keeps the SSH session alive even if the browser disconnects, and you can restore it later from the panel component. The stated goal is that compiles and long tasks are not interrupted by network instability.
How do I enable Passkey login in Nexus Terminal?
Set the `RP_ID` and `RP_ORIGIN` environment variables in the `.env` file. Login security also includes hCaptcha and Google reCAPTCHA integration plus two-factor authentication.
Does Nexus Terminal work on ARM?
Partially. arm64 users should replace `guacamole/guacd:latest` with `guacamole/guacd:1.6.0-RC1` in docker-compose.yml. On ARMv7, RDP is disabled because Apache Guacamole publishes no ARMv7 guacd image, and the README points to a separate compose file for that architecture. Debian amd64 is the recommended platform.
Where does Nexus Terminal store SSH credentials and how are they encrypted?
State lives in the `./data` directory mounted at `/app/data`. Sensitive fields such as SSH passwords, private keys and passkeys are encrypted with a 32-byte hex `ENCRYPTION_KEY` using AES-256, and `SESSION_SECRET` signs Express sessions. If neither is set, the system generates random keys on first startup and writes them to `data/.env`.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/heavrnl-nexus-terminal)Was this useful?