Self-hosted service
open-webui/open-terminal avatar
open-webui/open-terminal

open-terminal is a remote shell API whose default port, CORS and key handling are all wide open

A computer you can curl ⚡

3,578 stars335 forksPythonMIT

At a glance

What is it?
open-webui/open-terminal gives agents a place to run commands over HTTP, as a Python package or as one of four container images. The feature matrix is uneven across those images, the documented examples bind every network interface, and the API key can end up in container logs.
Who is it for?
Open Terminal fits one job well: giving an agent a machine it can run commands on, either inside a container you control or on a host you already trust. Before you deploy it, decide which of the two you are running, because the safeguards differ.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • open-webui/open-terminal3,578 ★ · +486
Star history
Repository2026-09-102026-11-14
open-webui/open-terminal3.1k3.6k

486 Stars in the last 65 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Four images, and the feature table is not flat

The container images differ in more than size. The latest image is about 4 GB and the other three sit between roughly 230 MB and 430 MB, but three rows of the table matter more than the weights. Runtime package installation works only on latest, which is also the only image that can run in multi-user mode. The egress firewall is present on latest, slim and alpine, and absent on openshift. Everything else in the table is bundled tooling, and slim and alpine are declared to have the same feature set, the difference being libc: slim is Debian with glibc for broader binary compatibility, alpine uses musl and is smaller, at the cost of some C extension pip packages having to compile from source. So the smallest image is not a smaller version of the same tool. It is the same tool with the ability to add packages, isolate users, and block outbound traffic taken away.

Every documented example binds all network interfaces

The recommended first command publishes the service and names a volume:

bash
docker run -d --name open-terminal --restart unless-stopped -p 8000:8000 -v open-terminal:/home/user -e OPEN_TERMINAL_API_KEY=your-secret-key ghcr.io/open-webui/open-terminal

Three defaults in that line are worth naming. The port mapping has no bind address, so Docker publishes 8000 on every interface of the host rather than only on loopback. The named volume open-terminal is where the container keeps its home directory. And the key is passed as an environment variable, which means it is visible to anything that can inspect the container's environment. The bare metal one-liners make the same choice in a different spelling, with --host 0.0.0.0 in both the uvx and the pip form of the run command. The sample TOML configuration repeats both patterns, setting host to 0.0.0.0 and cors_allowed_origins to a wildcard. The service is designed to be reachable, and every example reaches.

An unset API key is generated and printed to the logs

The tip under the first command says that if you do not set an API key, one is generated automatically, and that you can grab it with docker logs open-terminal. That is convenient for a first run and a poor default afterwards, because the container's log stream then holds a live credential for a shell. The documentation does offer a better arrangement for it: a note recommends putting the key in the user configuration file rather than on the command line, on the grounds that this keeps the key out of ps and htop, which is the same argument that applies to the log stream. The five level resolution order makes that possible, since a key set in a config file is overridden by nothing except a CLI flag you pass deliberately. The examples for the small images repeat the same pattern with a literal secret value in the environment, one line each for slim, alpine and openshift.

Mounting the Docker socket is root access to the host

The latest image bundles the Docker CLI, Compose and Buildx, and the documentation then shows how to hand the container the host's daemon socket:

bash
docker run -d --name open-terminal -p 8000:8000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v open-terminal:/home/user \
  ghcr.io/open-webui/open-terminal

The caution attached to it is unusually blunt, and it should be read as a property of the feature rather than a warning about a particular configuration. Mounting the socket gives the container full control over the host's Docker daemon, which is effectively root access, and anyone with access to the terminal can pull and run arbitrary containers including privileged ones, mount host directories, reach host networking, and manage every container on the host. The documented alternative is to fork the repository, edit the Dockerfile and build your own image, which is what the openshift variant represents in packaged form: it does not support Docker socket access, runtime package installs, the iptables egress firewall, or multi-user mode.

Packages are installed again on every container start

Extra tooling can be added without forking, through three environment variables: OPEN_TERMINAL_PACKAGES for a space separated list of apt packages, OPEN_TERMINAL_PIP_PACKAGES for pip packages, and OPEN_TERMINAL_NPM_PACKAGES for npm packages installed globally. All three run at startup, which means the cost is paid every time the container starts, so a long list makes each start slower and the documentation points at a custom image for heavy customization. The variables are also the first thing that breaks when you change variant. Slim, alpine and openshift ignore all three, and the note on that says to extend Dockerfile.slim or Dockerfile.alpine instead. The capability gap is also visible in the image itself: sudo is installed in the default image, which is why runtime package installation is marked as available there and nowhere else. The same asymmetry shows up for documents: latest carries LibreOffice for converting Word, Excel and PowerPoint files to PDF, while the other three stay minimal and need a custom image for that work.

Five levels decide which setting wins

Configuration can come from a TOML file, environment variables or CLI flags, resolved in a stated order with the highest priority first: CLI flags such as --host, --port and --api-key; then environment variables such as OPEN_TERMINAL_API_KEY; then a user config at $XDG_CONFIG_HOME/open-terminal/config.toml, which defaults to ~/.config/open-terminal/config.toml; then a system config at /etc/open-terminal/config.toml; and finally built-in defaults. Every key in the sample file is optional, and each one is worth reading for what it silently allows. host and port decide reachability. cors_allowed_origins is shown as a wildcard. execute_timeout is commented as the seconds to wait for command output and is unset by default, so a command that produces no output waits indefinitely. file_browser_root is set to home, and log_dir and binary_mime_prefixes round out the list. A specific file can be selected with open-terminal run --config /path/to/my-config.toml.

A terminal API that depends on an office document stack

The required dependencies of the Python package are not only the server ones. Alongside fastapi, uvicorn with the standard extra, click, httpx, python-multipart and aiofiles, the package requires pypdf, python-docx, openpyxl, python-pptx, striprtf and xlrd, which together cover PDF, Word, PowerPoint, Excel and RTF reading, plus nbclient and ipykernel for running notebooks. Those are mandatory installs rather than extras, so every deployment carries them even when nobody opens a spreadsheet in the session. Only pywinpty is conditional, marked for sys_platform equal to win32. Two optional pieces sit outside that list. An extra named mcp installs fastmcp at version 4.0.0 or newer, which is how the package can be driven as an MCP server, and it is not part of a plain install. The package itself requires Python 3.11 or newer, while the container pins a specific patch release of 3.12 for reproducible builds.

One version number, two one-line descriptions

The project describes itself twice with different words. The repository's short description calls it a computer you can curl, and the description field in pyproject.toml reads A remote terminal API. Both are accurate and neither tells you what the API surface is. The version does line up exactly: pyproject declares 0.14.0 and the newest release tag is v0.14.0, published the same day as the last push, 2026-09-23. The two before it are close together, v0.13.0 on 2026-09-14 and v0.12.5 on 2026-09-09, so three releases landed inside two weeks, which is worth knowing before you treat a version number as a slow moving target. The tree also explains the four images by listing four Dockerfiles and three entrypoint scripts, one of them named for the OpenShift path, plus a .python-version file and a SECURITY.md that the README does not discuss.

Editorial conclusion

Open Terminal fits one job well: giving an agent a machine it can run commands on, either inside a container you control or on a host you already trust. Before you deploy it, decide which of the two you are running, because the safeguards differ. The Docker path isolates commands, and the default image is the only one that can install packages at runtime, run in multi-user mode or apply the egress firewall. The bare metal path runs commands with your own permissions and the README says so in a caution of its own. Two defaults deserve an explicit override: the documented examples bind 0.0.0.0 and the sample config sets cors_allowed_origins to a wildcard, so pass a specific host and a restricted origin list before the port is reachable. And pass the API key yourself, because an unset key is generated and then printed in the container logs. Do not mount the Docker socket unless the environment is fully trusted; that step is root access to the host, and no amount of API key handling compensates for it.

Frequently asked questions

What is open-webui/open-terminal?

It is a lightweight, self-hosted terminal that gives AI agents and automation tools a dedicated environment to run commands, manage files and execute code through a REST API. It ships as a Python package named open-terminal requiring Python 3.11 or newer, and as four container images: latest, slim, alpine and openshift.

How do I install open-terminal with Docker?

Run docker run -d --name open-terminal --restart unless-stopped -p 8000:8000 -v open-terminal:/home/user -e OPEN_TERMINAL_API_KEY=your-secret-key ghcr.io/open-webui/open-terminal, then open http://localhost:8000. If you pass no key, one is generated automatically and can be read back with docker logs open-terminal.

Does open-terminal support MCP?

There is an optional extra for it. The project declares an mcp extra that installs fastmcp at version 4.0.0 or newer, which is what lets the terminal be driven as an MCP server. It is not part of the base dependency list, so a plain install does not bring it in.

Which configuration file does open-terminal read?

Settings resolve in this order: CLI flags, environment variables, the user config at $XDG_CONFIG_HOME/open-terminal/config.toml which defaults to ~/.config/open-terminal/config.toml, the system config at /etc/open-terminal/config.toml, then built-in defaults. A specific file can be selected with open-terminal run --config /path/to/my-config.toml.

Is mounting the Docker socket into open-terminal safe?

The project attaches a caution to that step: mounting the host Docker socket gives the container full control over the host's Docker daemon, which is effectively root access, and anyone with terminal access can run arbitrary containers including privileged ones, mount host directories and reach host networking. The openshift image variant does not support Docker socket access at all.

Official sources

  1. License: MIT
  2. open-webui/open-terminal on GitHub
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/open-webui-open-terminal.svg)](https://hysenlabs.com/projects/open-webui-open-terminal)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/open-webui-open-terminal.svg)](https://hysenlabs.com/projects/open-webui-open-terminal)

Was this useful?