# Nexterm: one web console for SSH, VNC, RDP, SFTP, Docker and Proxmox, with the engine split out

> The open source server management software is really three deployable pieces and a FlatBuffers protocol between them, and the environment variables are where most of the operational decisions live.

**gnmyt/Nexterm** — The open source server management software for SSH, VNC & RDP

- Repository: https://github.com/gnmyt/Nexterm
- Website: https://nexterm.dev
- Stars: 5,039 · Forks: 273
- Language: JavaScript
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/gnmyt-nexterm

## Six jobs in one console, listed by the project itself

The README describes Nexterm as open-source server management software and then enumerates what that means here. Connect remotely via SSH, VNC and RDP. Manage files through SFTP. Deploy applications via Docker. Manage Proxmox LXC and QEMU containers. Secure access with two-factor authentication and OIDC SSO. Separate users and servers into Organizations.

That list is the product pitch and it is also a fair description of scope. Each line replaces a distinct tool: a terminal client, a VNC and RDP viewer, an SFTP client, a Docker management UI, a Proxmox panel, and the auth work you would otherwise bolt onto each of those. The screenshots directory in the tree backs this up, with separate assets for servers, connections, sftp, snippets, monitoring and recordings.

The screenshots for snippets and recordings hint at two features the feature list does not mention. Snippets are saved commands, which is how a team stops retyping a deploy line. Recordings are session recordings, which is a reasonable audit feature for a tool whose whole job is holding credentials to your infrastructure.

The repository is JavaScript, MIT licensed, with the last push on 2026-09-22. The release tags are pre-1.0 in name only: v1.2.2-BETA on 2026-07-19, v1.2.1-BETA on 2026-05-24 and v1.2.0-BETA on 2026-01-01, so the numbering has moved well past 1.0 while the BETA suffix stayed on. JetBrains is credited in the README as the sponsor.

## Three images and the choice between them

The configuration section documents three Docker images and the difference between them is the first real decision a user faces:

| Image            | Description                                              |
|------------------|----------------------------------------------------------|
| `nexterm/aio`    | All-In-One, server, client, and engine bundled together |
| `nexterm/server` | Server and web client only, requires external engine     |
| `nexterm/engine` | Engine only                                              |

For a first install, `nexterm/aio` is the obvious answer because everything runs in one container. The split matters as soon as you care about network placement, which is the interesting case. The engine is the component that connects to your servers, so in a real deployment it wants to live somewhere with network reach to the machines, while the server and web client are the part your users touch and that you want behind your proxy.

The Dockerfile shows what the all-in-one image has to install to be that combination. It builds the engine as a stage, then adds a long list of Alpine packages to the server image: cairo, jpeg, libpng, ossp-uuid, pango, libwebp and openssl for rendering and TLS, then libpulse, libvorbis and libogg for audio, libssh2, libvncserver and freerdp-libs for the remote protocols, and libcurl plus util-linux and samba-client. It then copies the engine's graphics libraries across, including the DRI drivers, gbm, EGL and GL libraries, the vncclient shared object and the whole `/usr/local/lib/freerdp3/` tree, and runs `ldconfig` so the dynamic linker finds them.

That is a heavy image, and it is heavy because VNC and RDP need a graphics stack whether you use those features or not. The Dockerfile also copies two binaries out of the engine image, `nexterm-engine` and `nexterm-webview`, which tells you there is a browser-backed view path as well as the protocol clients.

The container exposes port 6989 and starts with `CMD ["/bin/sh", "docker-start.sh"]`, and there is a matching `docker-start.sh` at the root of the repository, so the entrypoint logic lives in a script rather than in the Dockerfile.

## Environment variables that decide how it behaves in production

The server listens on port 6989 by default, and the README lists the variables that change it. `SERVER_PORT` is the listening port, `CONTROL_PLANE_PORT` is the TCP port for engine communication with a default of 7800, `NODE_ENV` selects development or production, `LOG_LEVEL` takes system, info, verbose, debug, warn or error with system as default, and `ENCRYPTION_KEY` encrypts passwords, SSH keys and passphrases.

That key is the one to get right. The README says it supports Docker secrets, read from `/run/secrets/encryption_key`, which is the right way to supply it rather than baking it into an image. The shipped `.env.example` contains a literal example value:

```sh
ENCRYPTION_KEY=aba3aa8e29b9904d5d8d705230b664c053415c54be20ad13be99af0057dfa23a
SERVER_PORT=6989
HTTPS_PORT=5878
NODE_ENV=development
```

Copying that file verbatim and starting the server gives you a working install with a publicly known encryption key, which is the classic failure mode for this kind of product. The development instructions do say to make sure `ENCRYPTION_KEY` is set, and give the command to generate one:

```sh
openssl rand -hex 32
```

Two other variables reward attention. `AI_SYSTEM_PROMPT` appends extra instructions to the AI assistant's system prompt, with the README's own example being Always explain destructive commands before running them, which tells you an AI assistant is in scope and can act on servers. `TRUST_PROXY` takes a number of reverse proxies in front of Nexterm so logs and audits show the real client IP instead of the proxy's, with `1` for a single Nginx or Traefik, or a list of trusted addresses such as `10.10.10.10,192.168.0.0/16`. The README's warning on this one is direct: only enable it if your proxy sets `X-Forwarded-For`, otherwise clients can spoof their IP.

## Local development needs four runtimes and a generated schema

The development instructions are more demanding than the Docker path, and they explain why the repository has a `schema/` directory and a FlatBuffers compiler requirement.

The prerequisites are Node.js 18 or newer, Yarn, the FlatBuffers compiler `flatc`, and optionally Docker. The compiler installs per platform with `brew install flatbuffers` on macOS, `sudo apt install flatbuffers-compiler` on Ubuntu or Debian, and `winget install Google.FlatBuffers` on Windows.

Then clone and configure:

```sh
git clone https://github.com/gnmyt/Nexterm.git
cd Nexterm
```

```sh
cp .env.example .env
```

Dependencies are installed twice, once at the root and once in the client directory, which tells you this is not a single-package layout:

```sh
yarn install
cd client && yarn install
cd ..
```

The step that is easy to miss comes next, and the README marks it as required before starting the development server:

```sh
yarn schema:generate
```

After that, `yarn dev` runs the server, the client and a schema watcher together, and `yarn dev:engine` starts an engine separately. The README explains why that separation matters: the development server does not automatically start an engine, and to connect to servers an engine must be running separately. If you use local engine registration you set `LOCAL_ENGINE_TOKEN` in the server environment and use the same value as `REGISTRATION_TOKEN` for the engine, which is a shared secret handshake rather than an open port.

The package manifest is worth a look for the shape of the dependency set. It requires Node 22 or newer through its engines field, which is stricter than the Node.js 18 the README lists, and it carries bcrypt, sqlite3, sequelize and mysql2 for storage, openid-client, ldapts and speakeasy for authentication, express with express-ws and ws for the API, and archiver, decompress and webdav for file handling. There is also a `bin` entry named `ntctl`, so a command line client ships with the server package.

## The FlatBuffers boundary between server and engine

The most interesting technical decision in the repository is not visible in the README at all: server and engine talk over FlatBuffers, and the `schema/` directory plus the `schema:generate` build step exist to make that contract work.

That choice makes sense for this problem. The engine needs to receive credentials and commands and return streams of terminal output, file listings and protocol frames. FlatBuffers is built for exactly that shape of traffic, being schema-first, binary and cheap to parse incrementally, which matters when the payload is a stream of bytes arriving from a remote desktop rather than a JSON document.

The consequence for anyone extending the project is concrete. Adding a capability that crosses the boundary means editing a schema, running `yarn schema:generate`, and updating both sides. Capabilities that stay inside one process, such as a new panel in the client, do not. The presence of `client/`, `server/`, `engine/`, `connector/`, `cli/`, `mobile/`, `packaging/` and `landing/` in the tree shows the split is thorough: there is a separate connector component, a separate CLI, and a mobile directory.

The `scripts/` directory also holds shell scripts that the package manifest calls directly, including `build-guacd.sh` for building Guacamole's guacd and `build-engine.sh` for the engine. Building guacd from source in development is a hint about how the VNC and RDP support is assembled, and the Docker image installs libvncserver, freerdp-libs and freerdp3 libraries rather than bundling a single binary.

## Security posture, licensing and what the README does not say

The security section is a short list: two-factor authentication, session management, password encryption, Docker container isolation, and OAuth 2.0 OpenID Connect SSO. Two of those need elaboration from the dependency list. Two-factor authentication runs through `speakeasy`, which handles TOTP, and `@simplewebauthn/server` appears as a dependency, which points at WebAuthn or passkey support alongside the TOTP path. The `openid-client` and `ldapts` dependencies mean the SSO story covers both a generic OIDC provider and LDAP-backed directories.

The document is short because the README defers. Installation goes to docs.nexterm.dev, and the documentation link, the Discord invite at dc.gnmyt.dev, and separate issue links for bugs and feature requests are all the README offers on the way to fuller material.

Licensing is straightforward: distributed under the MIT license, with a `LICENSE` file at the root. That covers the server, the client and the engine, which matters if you intend to fork any single component.

Two gaps are worth naming plainly. First, there is nothing in the README about audit log retention, session timeout configuration, or how organizations map to actual infrastructure permissions, even though organizations and audit trails are both referenced. Second, an AI assistant that can run commands on your servers, configured through `AI_SYSTEM_PROMPT`, is a significant capability to enable without a documented permission model, and the README's own suggested prompt about explaining destructive commands suggests the authors are aware of the risk. If you are evaluating Nexterm for infrastructure where a mistake is expensive, the engine's permission model is the thing to read about on docs.nexterm.dev before you connect anything real to it.

## Conclusion

Nexterm makes sense for a small team that has drifted into a dozen separate tools for terminals, file transfer and container control, and wants one place with users, organizations, two-factor authentication and OIDC login in front of them. The architecture to understand before you deploy it is the three way split: `nexterm/aio` for a quick start, `nexterm/server` for the web app, and `nexterm/engine` for the component that actually opens connections, which is the piece that needs network reach to your machines. Two things to fix before production. `ENCRYPTION_KEY` encrypts passwords, SSH keys and passphrases, so a placeholder from `.env.example` must be replaced and then treated as unrecoverable if lost. And `TRUST_PROXY` should stay off unless your proxy genuinely sets `X-Forwarded-For`, since the README warns clients can spoof their IP otherwise. Read `docs.nexterm.dev/installation` next, because the README defers all install detail to it.

## FAQ

### What is Nexterm used for?

Nexterm is open source server management software that connects remotely via SSH, VNC and RDP, manages files through SFTP, deploys applications via Docker, and manages Proxmox LXC and QEMU containers. It adds two-factor authentication, OIDC SSO and an Organizations model for separating users and servers.

### What is the difference between the nexterm/aio, nexterm/server and nexterm/engine images?

The README describes nexterm/aio as server, client and engine bundled together, nexterm/server as server and web client only which requires an external engine, and nexterm/engine as engine only. The split matters when the component that opens connections to your servers needs different network placement from the web interface your users see.

### How do I install Nexterm with Docker?

The README does not give the compose file itself and points to the installation documentation at docs.nexterm.dev. What it does specify is that the server listens on port 6989, which the Dockerfile exposes, that the entrypoint runs docker-start.sh, and that three images exist: aio, server and engine.

### Which port does the Nexterm server listen on?

Port 6989 by default, changed with SERVER_PORT. A separate CONTROL_PLANE_PORT, default 7800, is the TCP port used for engine communication. The shipped .env.example also sets HTTPS_PORT to 5878 and NODE_ENV to development.

### What does ENCRYPTION_KEY do in Nexterm?

It is the encryption key for passwords, SSH keys and passphrases, and the README notes it supports Docker secrets read from /run/secrets/encryption_key. For development it suggests generating one with openssl rand -hex 32, and the shipped .env.example contains a placeholder value that should be replaced rather than reused.

## Sources

- [gnmyt/Nexterm on GitHub](https://github.com/gnmyt/Nexterm)
- [License: MIT](https://github.com/gnmyt/Nexterm/blob/main/LICENSE)
- [Project website](https://nexterm.dev)
- [README](https://github.com/gnmyt/Nexterm/blob/main/README.md)
- [Releases](https://github.com/gnmyt/Nexterm/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gnmyt-nexterm
