A modern, full-stack web-based SSH terminal client that enables secure connections to remote servers through your browser. This is a full stack application with React and Node.js, built with NestJS, Socket.IO, and xterm.js for a seamless terminal experience.
- SSH Terminal: Connect to remote servers through your browser
- Real-time Communication: Socket.IO powered terminal interface
- Theme Support: Multiple terminal themes with xterm.js addons
- Authentication: Username/password and SSH key support
- Responsive Design: Works on desktop and mobile devices
- Docker Ready: Single image deployment with Nginx reverse proxy
- Hot Reload: Development environment with live code updates
Monorepo Structure:
apps/api- NestJS backend with Socket.IO and SSH2apps/client- React frontend with xterm.js terminal- Single Docker image with Nginx reverse proxy for production
- Docker Compose setup for local development
Frontend: React 18, TypeScript, Vite, Tailwind CSS, xterm.js
Backend: NestJS, Socket.IO, ssh2, Node.js 22+
DevOps: Docker, pnpm, Turborepo, Nginx
# Run the complete application with a single command
docker run -p 8080:80 -p 8081:8081 kzamanbd/terminal:latest- Client Application:
http://localhost:8080 - API Server:
http://localhost:8081 - Health Check:
http://localhost:8080/health
-
Clone and install
git clone <repository-url> cd browser-terminal pnpm install
-
Start development servers
# Option 1: Direct development (recommended) pnpm dev # Start both API (8081) and client (5173) # Option 2: Docker Compose development pnpm docker:up # Start with Docker Compose
-
Access the application
- Client:
http://localhost:5173(Vite dev server) - API:
http://localhost:8081(NestJS server) - Health Check:
http://localhost:8081/health
- Client:
The project includes a complete Docker Compose setup for local development with hot reload:
# Start development environment
docker-compose up --build
# Start in background
docker-compose up -d --build
# Stop services
docker-compose down
# View logs
docker-compose logs -fDevelopment Services:
- API:
http://localhost:8081(NestJS with watch mode) - Client:
http://localhost:5173(Vite dev server) - Health Check:
http://localhost:8081/health
-
Connect to SSH Server
- Enter server details (host, port, username)
- Provide authentication (password or SSH key)
- Click connect to start terminal session
-
Use the Terminal
- Execute commands as in a regular terminal
- Switch themes and customize experience
- Real-time output with Socket.IO
pnpm dev- Start development servers (API + Client)pnpm build- Build for productionpnpm lint- Run lintingpnpm format- Format code with Prettierpnpm docker:up- Start with Docker Composepnpm docker:down- Stop Docker Compose servicespnpm docker:build- Build and push to Docker Hub
pnpm dev- Start with watch modepnpm start- Start production buildpnpm build- Build for productionpnpm test- Run tests
pnpm dev- Start Vite dev serverpnpm build- Build for productionpnpm preview- Preview production buildpnpm lint- Run ESLint
Image: kzamanbd/terminal:latest
# Run in production (single container)
docker run -d \
--name browser-terminal \
-p 80:80 \
-p 8081:8081 \
kzamanbd/terminal:latest
# With custom ports
docker run -d \
--name browser-terminal \
-p 8080:80 \
-p 8081:8081 \
kzamanbd/terminal:latest# Build and push new version
./scripts/docker-build-push.sh v1.0.0
# Or use npm script
pnpm docker:build- Single Image: Contains both API and client
- Nginx Reverse Proxy: Serves client and proxies API requests
- Health Monitoring: Built-in health check endpoint
- Optimized Build: Multi-stage Docker build for minimal size
- Process Management: Uses tini for proper signal handling
Create a docker-compose.prod.yml file:
services:
browser-terminal:
image: kzamanbd/terminal:latest
container_name: browser-terminal
restart: unless-stopped
ports:
- "80:80" # Client (nginx)
- "8081:8081" # API (direct access)
environment:
- NODE_ENV=production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
networks:
- app-network
networks:
app-network:
driver: bridgeDeploy with:
docker-compose -f docker-compose.prod.yml up -dCreate k8s-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: browser-terminal
spec:
replicas: 2
selector:
matchLabels:
app: browser-terminal
template:
metadata:
labels:
app: browser-terminal
spec:
containers:
- name: browser-terminal
image: kzamanbd/terminal:latest
ports:
- containerPort: 80
- containerPort: 8081
env:
- name: NODE_ENV
value: "production"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: browser-terminal-service
spec:
selector:
app: browser-terminal
ports:
- name: http
port: 80
targetPort: 80
- name: api
port: 8081
targetPort: 8081
type: LoadBalancerDeploy with:
kubectl apply -f k8s-deployment.yaml| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | production |
PORT |
API server port | 8081 |
The application provides health check endpoints:
- Client Health:
GET /health- Returns "healthy" - API Health:
GET /health- Returns{"status":"ok"}
Monitor your deployment:
# Check container health
docker ps
docker logs browser-terminal
# Check API health
curl http://localhost:8081/health
# Check client health
curl http://localhost/health-
Port Conflicts
# Check what's using the ports lsof -i :80 lsof -i :8081 # Use different ports docker run -p 8080:80 -p 8081:8081 kzamanbd/terminal:latest
-
Container Won't Start
# Check logs docker logs browser-terminal # Check container status docker ps -a
-
API Not Responding
# Test API directly curl http://localhost:8081 # Check if container is running docker exec -it browser-terminal ps aux
-
Client Not Loading
# Check nginx status docker exec -it browser-terminal nginx -t # Check if files are served curl -I http://localhost
-
Dependencies Not Installing
# Clear Docker cache docker system prune -a # Rebuild without cache docker-compose build --no-cache
-
Hot Reload Not Working
# Ensure volume mounts are correct docker-compose config # Check file permissions docker-compose exec api ls -la /app/apps/api
MIT License - see LICENSE for details.
Kamruzzaman