Skip to content

Sammy-T/ts-term

Repository files navigation

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 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 and their 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.

Screenshot 2025-08-11 073307

Getting Started

Running with Docker run

Create a container network:

docker network create ts-term-net

Pull and run the container with the command:

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 <tailscale-domain>: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.
<host-port>:<container-port>
-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.

See https://docs.docker.com/reference/cli/docker/container/run/ for full reference.

Running with Docker Compose

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

docker compose up -d

Then visit your host machine's <tailscale-domain>: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. <user-home>/.ssh/known_hosts

Development

Run the dev server

Requirements:

Create a .env file in the project root if you want to customize ts-term. See environment variables.

Add Go dependencies

go get ./...

Run the dev server

go run . -dev

Then visit localhost:3000 while logged in to your tailnet.

Build and run the Docker container

Build the image

docker build --platform=linux/amd64 -t my/ts-term .

Create and run a container from the image

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 <tailscale-domain>:4000.