https://github.com/sammy-t/ts-term
Browser-based SSH sessions using ephemeral Tailscale nodes.
https://github.com/sammy-t/ts-term
bash cli command-line docker self-hosted shell ssh tailscale terminal tsnet web-app
Last synced: 3 months ago
JSON representation
Browser-based SSH sessions using ephemeral Tailscale nodes.
- Host: GitHub
- URL: https://github.com/sammy-t/ts-term
- Owner: Sammy-T
- License: gpl-3.0
- Created: 2025-08-11T03:40:01.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2026-02-11T04:10:14.000Z (4 months ago)
- Last Synced: 2026-02-11T08:54:03.829Z (4 months ago)
- Topics: bash, cli, command-line, docker, self-hosted, shell, ssh, tailscale, terminal, tsnet, web-app
- Language: Go
- Homepage: https://hub.docker.com/r/sammytd/ts-term
- Size: 278 KB
- Stars: 8
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ts-term
ts-term serves a browser-based terminal interface and provides SSH through ephemeral Tailscale nodes.
ts-term is intended to be hosted from inside a [Docker](https://www.docker.com/) container on a machine in your tailnet. This way, you can access the service through your tailnet.
Each session runs through ephemeral Tailscale nodes created under the user's identity.
[Tailscale SSH](https://tailscale.com/kb/1193/tailscale-ssh) and their [SSH Console](https://tailscale.com/kb/1216/tailscale-ssh-console) are great tools for remote access to machines on your tailnet. If they suit your needs, I definitely recommend them.
However, I wanted something I could self-host and which didn't take over my machine's `ssh` command so I made this project.

## Getting Started
### Running with Docker run
Create a container network:
```bash
docker network create ts-term-net
```
Pull and run the container with the command:
```bash
docker run -d -p 4000:3000 -h ts-term --name ts-term --network=ts-term-net -v ts-term-data:/home/appuser/.ssh sammytd/ts-term
```
Then visit your host machine's `:4000` from a browser on a device logged in to your tailnet.
Close the window or type `exit` while not using SSH to end the session.
#### Docker run options
| Option | Description |
| --- | --- |
| `-d, --detach` | Run the container in the background. |
| `-p, --publish` | Publish the container's port to the host. The host port can be whichever available port you want.
`:` |
| `-h, --hostname` | The container host name. (optional) |
| `--name` | The container name (optional) |
| `--network` | The container network to use. (recommended) |
| `-v, --volume` | The container volume for persistent data. (recommended) |
| `--env-file` | Read in a file of environment variables. (optional)
See [environment variables](#environment-variables). |
See for full reference.
### Running with Docker Compose
```yaml
# compose.yml
name: ts-term
services:
ts-term:
image: sammytd/ts-term
hostname: ts-term
# env_file: .env
ports:
- "4000:3000"
networks:
- ts-term-net
volumes:
- ts-term-data:/home/appuser/.ssh
restart: unless-stopped
volumes:
ts-term-data:
networks:
ts-term-net:
name: ts-term-net
```
Pull and run the container with the command:
```bash
docker compose up -d
```
Then visit your host machine's `:4000` from a browser on a device logged in to your tailnet.
Close the window or type `exit` while not using SSH to end the session.
### Environment Variables
| Variable | Description | Default |
| --- | --- | --- |
| TS_TERM_ADDR | The address the ts-term server runs on. | `:3000` |
| TS_CONTROL_URL | The coordination server to use. | The default Tailscale server |
| TS_TERM_KNOWN_HOSTS | The absolute path to the known_hosts file. | `/.ssh/known_hosts` |
## Development
### Run the dev server
Requirements:
- [Node.js](https://nodejs.org/)
- [pnpm](https://pnpm.io/) (optional)
- [Go](https://go.dev/)
Create a `.env` file in the project root if you want to customize ts-term. See [environment variables](#environment-variables).
#### Add Go dependencies
```bash
go get ./...
```
#### Run the dev server
```bash
go run . -dev
```
Then visit `localhost:3000` while logged in to your tailnet.
### Build and run the Docker container
#### Build the image
```bash
docker build --platform=linux/amd64 -t my/ts-term .
```
#### Create and run a container from the image
```bash
docker run -d -p 4000:3000 -h ts-term --name my-ts-term -v ts-term-data:/home/appuser/.ssh my/ts-term
```
Then visit your host machine's `:4000`.