Tabby Web provides the Tabby terminal in a browser and can also synchronize Tabby desktop configurations. This guide deploys the official prebuilt image with Docker Compose, MariaDB, GitHub OAuth, persistent storage, and an HTTPS reverse proxy.
The upstream maintainer currently has limited time to support Tabby Web. The official container also retains an older runtime. Review the project status, test upgrades before production use, and avoid exposing an unmaintained deployment to sensitive networks without additional controls.
Official references:
- Tabby Web repository
- Tabby Web deployment guide
- Tabby Web container registry
- Tabby Web application versions
- Docker Engine installation on Ubuntu
Environment
The examples use the following values:
| Setting | Example |
|---|---|
| Server operating system | Ubuntu 24.04 LTS |
| Public hostname | tabby.example.com |
| Local Tabby Web address | 127.0.0.1:9090 |
| Installation directory | /opt/tabby-web |
| Authentication provider | GitHub OAuth |
Create a public DNS record for tabby.example.com before configuring HTTPS. Allow inbound TCP ports 80 and 443 to the reverse proxy. The application port remains bound to localhost and should not be published directly to the Internet.
Install Docker Engine
If Docker Engine and the Docker Compose plugin are already installed, verify them and continue to the next section.
sudo docker version
sudo docker compose version
On a clean Ubuntu 24.04 LTS server, install Docker from Docker’s official repository.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo docker run --rm hello-world
Membership in the docker group grants privileges equivalent to root. This guide keeps sudo in the Docker commands instead of changing group membership.
Register a GitHub OAuth App
Open GitHub Developer Settings, select OAuth Apps, and create a new OAuth app.
Use these values:
| Field | Value |
|---|---|
| Application name | Tabby Web |
| Homepage URL | https://tabby.example.com |
| Authorization callback URL | https://tabby.example.com/api/1/auth/social/complete/github/ |
Copy the Client ID, generate a Client secret, and store both in a password manager. The callback URL must match exactly, including the /api/1/auth/social/complete/github/ path and trailing slash.
Create the Deployment Files
Create the project directory.
sudo install -d -m 0750 /opt/tabby-web
sudo chown "$USER":"$USER" /opt/tabby-web
cd /opt/tabby-web
Generate three hexadecimal secrets. Hexadecimal database passwords do not require URL encoding inside DATABASE_URL.
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32
Create /opt/tabby-web/.env and replace every placeholder with the appropriate generated secret or GitHub OAuth value.
nano /opt/tabby-web/.env
DB_PASSWORD=replace_with_the_first_generated_value
DB_ROOT_PASSWORD=replace_with_the_second_generated_value
DJANGO_SECRET_KEY=replace_with_the_third_generated_value
SOCIAL_AUTH_GITHUB_KEY=replace_with_the_github_client_id
SOCIAL_AUTH_GITHUB_SECRET=replace_with_the_github_client_secret
Restrict access to the secrets file.
chmod 600 /opt/tabby-web/.env
Create /opt/tabby-web/compose.yaml.
nano /opt/tabby-web/compose.yaml
services:
db:
image: mariadb:10.11
restart: unless-stopped
environment:
MARIADB_DATABASE: tabby
MARIADB_USER: tabby
MARIADB_PASSWORD: ${DB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: [CMD, healthcheck.sh, --connect, --innodb_initialized]
interval: 10s
timeout: 5s
retries: 10
tabby:
image: ghcr.io/eugeny/tabby-web:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:9090:80"
environment:
DATABASE_URL: mysql://tabby:${DB_PASSWORD}@db:3306/tabby
PORT: "80"
DOCKERIZE_ARGS: -wait tcp://db:3306 -timeout 60s
APP_DIST_STORAGE: file:///app-dist
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY}
SOCIAL_AUTH_GITHUB_KEY: ${SOCIAL_AUTH_GITHUB_KEY}
SOCIAL_AUTH_GITHUB_SECRET: ${SOCIAL_AUTH_GITHUB_SECRET}
volumes:
- app-dist:/app-dist
volumes:
app-dist:
db-data:
Do not add DEBUG=False to this deployment. The current upstream settings convert any nonempty DEBUG value to true, so the literal string False unexpectedly enables Django debug mode. Leaving the variable unset keeps debug mode disabled.
The original sample also used SQLite and exposed port 8000. The current official image listens on container port 80, and MariaDB is a better choice for a persistent multi-user deployment.
Validate the Compose file before starting the containers.
cd /opt/tabby-web
sudo docker compose config --quiet
The expanded output of docker compose config contains substituted secrets. Do not redirect or publish it.
Start Tabby Web
Pull the images and start the services.
cd /opt/tabby-web
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
Check the application locally.
curl -fsS http://127.0.0.1:9090/api/1/auth/providers
sudo docker compose logs --tail=100 tabby
The providers endpoint should return JSON that includes GitHub. If it returns an empty list, recheck the two SOCIAL_AUTH_GITHUB_* values and recreate the application container.
sudo docker compose up -d --force-recreate tabby
Add a Tabby Web Application Version
The backend stores browser application distributions separately from the server image. Check the available versions of tabby-web-container, then add the selected version. The following version was available when this guide was updated.
cd /opt/tabby-web
sudo docker compose exec tabby /manage.sh add_version 1.0.231-nightly.0
sudo docker compose exec tabby ls -lah /app-dist
The app-dist volume preserves these files when the Tabby Web container is replaced.
Configure HTTPS with a Reverse Proxy
Tabby Web handles authentication tokens and synchronized terminal settings, so use HTTPS outside a local test environment. Keep port 9090 bound to 127.0.0.1 and proxy the public hostname to it.
For Caddy, add this site block to /etc/caddy/Caddyfile:
tabby.example.com {
reverse_proxy 127.0.0.1:9090
}
Validate and reload Caddy.
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
Caddy obtains and renews the TLS certificate automatically when DNS is correct and TCP ports 80 and 443 reach the server. Other reverse proxies must preserve the Host, X-Forwarded-For, and X-Forwarded-Proto headers and support WebSocket upgrades.
Open https://tabby.example.com, select the login button, and authenticate with GitHub. If GitHub reports a callback mismatch, compare the configured callback URL character by character with:
https://tabby.example.com/api/1/auth/social/complete/github/
Configure Tabby Desktop Synchronization
After signing in to Tabby Web, open its settings and copy the Config sync token. Treat this token like a password.
Before the first synchronization, open Settings → Config file in the Tabby desktop app and create a backup. Then open Settings → Config Sync and enter:
| Field | Value |
|---|---|
| Sync host | https://tabby.example.com |
| Secret sync token | The token copied from Tabby Web |
Do not add /api/1/ or a trailing API path to the sync host. Tabby adds its API paths automatically.
For the first device, choose the option that uploads the known-good local configuration as a new remote configuration. On additional devices, choose the intended remote configuration and verify the download direction before overwriting local settings. Enable automatic synchronization only after confirming that the correct configuration exists on both sides.
Optional: Browser SSH and Telnet Connections
Configuration synchronization does not require a connection gateway. Browser-based SSH and Telnet connections do require one because a browser cannot open arbitrary TCP connections directly.
Tabby Web can use the hosted gateway, but connection traffic then passes through that external service. For sensitive environments, deploy the separate Tabby Connection Gateway, protect it with a strong token, and publish it through HTTPS and WebSockets.
Back Up the Deployment
Back up both named volumes and the protected configuration files. A database backup is safer than copying a live database volume.
cd /opt/tabby-web
sudo docker compose exec -T db sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction --databases tabby' > tabby-db.sql
sudo tar -C /opt/tabby-web -czf tabby-web-config.tar.gz .env compose.yaml
Also back up the app-dist volume or retain a record of every version added with manage.sh so it can be recreated.
Update Tabby Web
Back up the database and configuration first. Then pull the current images and recreate the services.
cd /opt/tabby-web
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=100 tabby
Because latest is a moving tag and the project has limited maintenance, test the update with a restored copy of the data before applying it to an important deployment. For reproducible production updates, replace latest with a tested image digest.
Troubleshooting
No Authentication Providers Are Shown
Check that the OAuth variables reached the application container without printing their values.
cd /opt/tabby-web
sudo docker compose exec tabby sh -c 'env | cut -d= -f1 | grep ^SOCIAL_AUTH_'
curl -fsS http://127.0.0.1:9090/api/1/auth/providers
GitHub Reports a Callback Error
The callback registered in GitHub must use HTTPS, the correct hostname, and the complete path /api/1/auth/social/complete/github/. Also verify that the reverse proxy sends X-Forwarded-Proto: https.
The Page Is Blank or an Application Version Is Missing
Inspect the persisted distribution directory and add a valid version from the npm package list.
cd /opt/tabby-web
sudo docker compose exec tabby ls -lah /app-dist
sudo docker compose logs --tail=200 tabby
The Database Is Unavailable
Check the database health and review both service logs.
cd /opt/tabby-web
sudo docker compose ps
sudo docker compose logs --tail=200 db
sudo docker compose logs --tail=200 tabby
sudo docker compose exec tabby python -c "import django; django.setup(); from django.db import connection; connection.ensure_connection(); print('Database connection succeeded')"
Configuration Sync Does Not Start
Confirm that the sync host contains only the HTTPS origin, copy a fresh token from the authenticated Tabby Web settings, and verify that the desktop clock is correct. Restore the local configuration backup if the wrong synchronization direction overwrites the device.