Self-Hosting Deployment Guide
| Method | Use Case | Dependencies | HTTPS |
|---|---|---|---|
| Tier 1 — Single Binary | Local/intranet/development/quick trial | None (single file) | None (HTTP) |
| Tier 2 — Docker Single Container | Quick deploy/testing/intranet | Docker | None (HTTP) |
| Tier 3 — Docker Compose | Public-facing production | Docker + Compose v2 | Automatic (Let’s Encrypt) |
Zero-knowledge architecture remains unchanged — Regardless of deployment method, the server only relays encrypted ciphertext and never stores any message content.
Two Dockerfiles in the Project
| File | Purpose | Includes Frontend | Default Port | Build Context |
|---|---|---|---|---|
deploy/Dockerfile | Self-hosting (Tier 2/3, recommended) | ✅ Three-stage build | 8080 | Project root |
arthas-server/Dockerfile | HF Spaces backend-only relay | ❌ Backend only | 7860 | arthas-server/ |
Key difference:
deploy/Dockerfilebuilds the frontend first (Node.js), then embeds the output into the Go binary. The final image contains the complete frontend UI + WebSocket backend.arthas-server/Dockerfileonly compiles the Go backend without building the frontend. Used for split deployment (frontend on Vercel, backend on HF Spaces).
⚠️ Common mistake: If you build with
docker build -t arthas-server ./arthas-server, visiting the root URL will show a blank page (placeholder HTML). This is because you used the wrong Dockerfile. For self-hosting, usedeploy/Dockerfile.
Prerequisites
Minimum Hardware Requirements
| Resource | Minimum | Notes |
|---|---|---|
| CPU | 1 core | Go server has very low resource usage |
| Memory | 512 MB | Including OS overhead |
| Disk | 1 GB | Including Docker images and certificate storage |
| Network | Ports 80 + 443 open | Required for Tier 3 public deployment (Tier 1/2 only needs a custom port) |
Tier 1 Prerequisites
- No additional dependencies — download the binary and run
Tier 2 Prerequisites
- Docker Engine 20.10+
docker --version # Docker version 20.10+Tier 3 Prerequisites
- Docker Engine 20.10+
- Docker Compose v2 (
docker composeplugin, not the standalonedocker-compose) - A domain name pointing to your server’s IP (for public deployment)
- Server firewall with ports 80 and 443 open
docker --version # Docker version 20.10+docker compose version # Docker Compose version v2.x.xQuick Start: Tier 1 (Single Binary)
Suitable for local testing, intranet deployment, or development. The Go binary embeds frontend static files — download and run.
1. Download the Binary
Download the binary for your platform from GitHub Releases — choose the arthas-server-all variant (includes frontend + backend):
# Linux (x86_64)curl -Lo arthas-server-all https://github.com/michaelwang123/arthas/releases/latest/download/arthas-server-all-linux-amd64chmod +x arthas-server-all
# Linux (ARM64, e.g. Raspberry Pi)curl -Lo arthas-server-all https://github.com/michaelwang123/arthas/releases/latest/download/arthas-server-all-linux-arm64chmod +x arthas-server-all
# macOS (Apple Silicon)curl -Lo arthas-server-all https://github.com/michaelwang123/arthas/releases/latest/download/arthas-server-all-darwin-arm64chmod +x arthas-server-all
# macOS (Intel)curl -Lo arthas-server-all https://github.com/michaelwang123/arthas/releases/latest/download/arthas-server-all-darwin-amd64chmod +x arthas-server-allBinary variants:
arthas-server-all-*— Includes frontend + backend (recommended for self-hosting, ~7MB)arthas-server-*— Backend WebSocket relay only (for split deployment)
2. Start the Service
# Default port 8080./arthas-server-all
# Custom port./arthas-server-all --port 3000
# Restrict WebSocket origins (recommended for production)./arthas-server-all --port 443 --allowed-origins "https://chat.example.com"3. Access
Open your browser and navigate to http://localhost:8080 (or the port you specified) to start using Arthas.
CLI Parameters
| Parameter | Default | Description |
|---|---|---|
--port | 8080 (or $PORT env variable) | HTTP listening port |
--allowed-origins | * (allow all origins) | WebSocket CORS whitelist, comma-separated for multiple values |
--version | — | Print version and exit |
Note: Tier 1 does not provide HTTPS. For public HTTPS deployment, use Tier 3 or configure a reverse proxy yourself.
Quick Start: Tier 2 (Docker Single Container)
Suitable for quick deployment, intranet use, or testing. Pre-built images are available on GitHub Container Registry — one command to run.
Option A: Use Pre-built Image (Recommended)
# Pull and run directly (image < 30MB, includes full frontend + backend)docker run -d -p 8080:8080 --name arthas ghcr.io/michaelwang123/arthas:latest
# Custom portdocker run -d -p 3000:3000 -e PORT=3000 --name arthas ghcr.io/michaelwang123/arthas:latest
# Restrict CORS originsdocker run -d -p 8080:8080 -e ALLOWED_ORIGINS=https://chat.example.com --name arthas ghcr.io/michaelwang123/arthas:latestOr use Docker Compose:
services: arthas: image: ghcr.io/michaelwang123/arthas:latest ports: - "8080:8080" restart: unless-stoppeddocker compose up -dOption B: Build from Source
If you need custom modifications:
git clone https://github.com/michaelwang123/arthas.gitcd arthas
# Build from project root (must use deploy/Dockerfile)docker build -f deploy/Dockerfile -t arthas-server .
# Startdocker run -d -p 8080:8080 --name arthas arthas-server4. Verify
# Health checkcurl http://localhost:8080/ping# Returns "pong"
# Check container status (shows "healthy" after ~30 seconds)docker ps --filter name=arthas# STATUS: Up X seconds (healthy)
# View logsdocker logs arthas# [2024-01-01T12:00:00Z] [INFO] [Server] started on :8080 (version latest)5. Access
Open your browser and navigate to http://localhost:8080 to see the full frontend and start using Arthas.
Container routes:
| Path | Function |
|---|---|
/ | Frontend SPA page (index.html) |
/assets/* | Frontend static assets (JS/CSS, with content-hash long-term caching) |
/ws | WebSocket connection endpoint |
/ping | Health check |
6. Stop and Cleanup
# Stop the containerdocker stop arthas
# Remove the containerdocker rm arthas
# Remove the image (optional)docker rmi ghcr.io/michaelwang123/arthas:latestDocker Compose Single Container Mode
If you prefer using Docker Compose for management:
services: arthas: image: ghcr.io/michaelwang123/arthas:latest ports: - "8080:8080" environment: - ALLOWED_ORIGINS=* restart: unless-stoppeddocker compose up -dQuick Start: Tier 3 (Docker Compose + HTTPS)
Suitable for public-facing production environments. Caddy automatically obtains Let’s Encrypt certificates — deploy with a single command.
1. Clone the Repository
git clone https://github.com/michaelwang123/arthas.gitcd arthas/deploy2. Run the Deployment Script
./deploy.shThe script will interactively guide you through the configuration:
[INFO] Checking prerequisites...[✓] Docker is installed and running[✓] Docker Compose v2 is available[✓] Ports 80 and 443 are available
Enter your domain name (e.g., chat.example.com): chat.example.comEnter your email for Let's Encrypt: admin@example.comEnter your GitHub username (image registry): your-username
[✓] Configuration saved to .env[✓] Caddyfile generated (production mode)[INFO] Starting services...[✓] Arthas is running at https://chat.example.com3. Access
After deployment is complete, open your browser and navigate to https://your-domain to start using Arthas.
Local Mode (Testing Without a Domain)
If you just want to quickly try the Docker Compose deployment locally:
./deploy.sh --localThis automatically sets DOMAIN=localhost, uses HTTP (no HTTPS), and is accessible at http://localhost.
Deployment Script Command Reference
deploy.sh supports the following subcommands:
| Command | Description |
|---|---|
./deploy.sh | Full deployment flow (check → configure → start) |
./deploy.sh --local | Local HTTP mode deployment (DOMAIN=localhost) |
./deploy.sh --status | View health status of all services |
./deploy.sh --logs | View the last 50 lines of container logs |
./deploy.sh --upgrade | Pull latest images and restart services |
./deploy.sh --down | Stop and remove all containers |
./deploy.sh --reconfigure | Delete config files and re-enter interactive setup |
Examples
# Check service status./deploy.sh --status# Output:# arthas-backend: healthy (running)# arthas-caddy: healthy (running)
# View logs to troubleshoot issues./deploy.sh --logs
# Upgrade to latest version./deploy.sh --upgrade
# Stop services./deploy.sh --down
# Switch domains (stop first, then reconfigure)./deploy.sh --down./deploy.sh --reconfigureConfiguration Reference
All configuration is managed through the deploy/.env file. It is interactively generated on the first run of deploy.sh.
| Variable | Required | Default | Description |
|---|---|---|---|
DOMAIN | Yes | — | Deployment domain. Use your actual domain for public (e.g., chat.example.com), or localhost for local |
EMAIL | Required for public | — | Let’s Encrypt certificate registration email. Can be left empty when DOMAIN=localhost |
ARTHAS_VERSION | No | latest | Docker image version tag (e.g., v1.0.0). Recommended to pin a specific version in production |
GITHUB_OWNER | Yes | — | GitHub username/organization, used to construct image address ghcr.io/{GITHUB_OWNER}/arthas |
ALLOWED_ORIGINS | No | Auto-generated | WebSocket CORS whitelist. Automatically set to https://{DOMAIN} for public, * for local |
Manual Configuration
# Edit the .env filevim deploy/.env
# Regenerate Caddyfile and restart./deploy.sh --reconfigureUpgrading
Tier 3 (Docker Compose) Upgrade
cd deploy
# Option 1: Upgrade to latest./deploy.sh --upgrade
# Option 2: Upgrade to a specific version# 1. Update ARTHAS_VERSION in .envsed -i 's/ARTHAS_VERSION=.*/ARTHAS_VERSION=v1.2.0/' .env# 2. Pull new image and restart./deploy.sh --upgradeTier 2 (Docker Single Container) Upgrade
# Pull latest image and replace containerdocker pull ghcr.io/michaelwang123/arthas:latestdocker rm -f arthasdocker run -d -p 8080:8080 --name arthas ghcr.io/michaelwang123/arthas:latestTier 1 (Single Binary) Upgrade
# 1. Stop the current servicekill $(pgrep arthas-server)
# 2. Download new version (overwrite old file)curl -Lo arthas-server-all https://github.com/michaelwang123/arthas/releases/latest/download/arthas-server-all-linux-amd64chmod +x arthas-server-all
# 3. Restart./arthas-server-all --port 8080Tip: Arthas is a stateless service (no message storage), so upgrades do not involve data migration.
Backup
Caddy Certificate Backup (Tier 3)
Caddy’s TLS certificates are stored in the Docker named volume caddy_data. Regular backups are recommended to avoid hitting Let’s Encrypt rate limits when requesting new certificates.
# Backup the certificate volumedocker run --rm \ -v arthas_caddy_data:/data \ -v $(pwd)/backup:/backup \ alpine tar czf /backup/caddy-certs-$(date +%Y%m%d).tar.gz /data
# Restore the certificate volumedocker run --rm \ -v arthas_caddy_data:/data \ -v $(pwd)/backup:/backup \ alpine sh -c "cd / && tar xzf /backup/caddy-certs-20240101.tar.gz"Tier 1/2 Backup
Tier 1 and Tier 2 are stateless — no backup needed. Binaries can be re-downloaded anytime, Docker images can be rebuilt anytime.
Troubleshooting
Blank Page After Docker Run
Symptoms: After docker run, visiting http://localhost:8080 shows a blank page or only <div id="root"></div>.
Cause: Used the wrong Dockerfile. arthas-server/Dockerfile does not include the frontend build step.
Solution:
# Wrong ❌ (backend only, no frontend)docker build -t arthas-server ./arthas-server
# Correct ✅ (use pre-built image with full frontend + backend)docker run -d -p 8080:8080 ghcr.io/michaelwang123/arthas:latest
# Or build from source (using deploy/Dockerfile)docker build -f deploy/Dockerfile -t arthas-server .Port Already Allocated
Symptoms: docker run reports port is already allocated.
Troubleshooting:
# Linux/macOSlsof -i :8080
# Windowsnetstat -ano | findstr "8080"
# Check if another Docker container is using the portdocker ps --filter "publish=8080"Solution:
# Remove the container using the portdocker rm -f <container_name>
# Or use a different portdocker run -d -p 9090:9090 -e PORT=9090 --name arthas arthas-serverDNS Not Propagated (Tier 3)
Symptoms: After deployment, visiting the domain shows “This site can’t be reached”, Caddy logs show ACME challenge failure.
Troubleshooting:
# Check if DNS resolves to your server IPdig +short chat.example.com
# Verify using a specific DNS serverdig @8.8.8.8 chat.example.comSolution:
- Confirm the domain’s DNS A record points to your server IP
- Wait for DNS propagation (usually 5-30 minutes, up to 48 hours)
- After DNS is effective, restart Caddy:
./deploy.sh --down && ./deploy.sh
Port Conflict (Tier 3)
Symptoms: deploy.sh reports port 80 or 443 is occupied.
Troubleshooting:
# Linuxsudo ss -tlnp | grep ':80 'sudo ss -tlnp | grep ':443 '
# macOSsudo lsof -i :80 -sTCP:LISTENsudo lsof -i :443 -sTCP:LISTENCommon processes and solutions:
| Process | Solution |
|---|---|
| nginx | sudo systemctl stop nginx && sudo systemctl disable nginx |
| apache2/httpd | sudo systemctl stop apache2 && sudo systemctl disable apache2 |
| Another Caddy instance | sudo systemctl stop caddy |
Certificate Request Failed (Tier 3)
Symptoms: Caddy logs show ACME challenge failure, HTTPS unavailable.
Solution:
- Confirm port 80 is open to the internet (Let’s Encrypt HTTP-01 validation requires it)
- Confirm DNS correctly resolves to your server
- Check server firewall rules:
Terminal window # Ubuntu/Debiansudo ufw allow 80/tcpsudo ufw allow 443/tcp# CentOS/RHELsudo firewall-cmd --permanent --add-service=httpsudo firewall-cmd --permanent --add-service=httpssudo firewall-cmd --reload
Unhealthy Container
Symptoms: docker ps shows container as unhealthy.
Troubleshooting:
# Check health statusdocker inspect arthas --format='{{.State.Health.Status}}'
# Manually test health endpointdocker exec arthas wget -qO- http://localhost:8080/ping# Should return "pong"
# View logsdocker logs arthasARM64 Compatibility
Arthas Docker images support both linux/amd64 and linux/arm64. Docker automatically pulls the matching image.
uname -m # aarch64 = ARM64, x86_64 = AMD64docker build -f deploy/Dockerfile -t arthas-server .Architecture Overview
Tier 1 / Tier 2 Architecture
Browser ──HTTP/WS──→ Go Service (port 8080) ├── Static file server (embedded dist/) ├── WebSocket Hub (message relay) └── /ping health checkTier 3 Architecture
Browser ──HTTPS──→ Caddy (port 443) │ │ (reverse proxy) ↓ Go Container (internal port 8080) ├── Static file server (embedded dist/) ├── WebSocket Hub (message relay) └── /ping health checkCaddy handles:
- HTTPS termination and automatic certificate renewal
- HTTP → HTTPS redirect
- Reverse proxy all requests to Go backend
- Security response header injection
Go backend handles:
- Serving frontend static files (SPA route fallback)
- WebSocket message relay
- Health check endpoint (
/ping)
Building from Source
Docker Build (For Custom Modifications)
git clone https://github.com/michaelwang123/arthas.gitcd arthas
# Build the full image (frontend + backend)docker build -f deploy/Dockerfile -t arthas-server .
# Specify version numberdocker build -f deploy/Dockerfile --build-arg VERSION=v1.2.3 -t arthas-server:v1.2.3 .
# Rundocker run -d -p 8080:8080 --name arthas arthas-serverNote: For most users, the pre-built image
ghcr.io/michaelwang123/arthas:latestis recommended — no need to clone or build.
Local Build (Requires Go + Node.js)
git clone https://github.com/michaelwang123/arthas.gitcd arthas
# Build all platform binaries (requires Go 1.23+ and Node.js 18+)make build-all
# Output in dist/ directory:# dist/arthas-server-all-linux-amd64# dist/arthas-server-all-linux-arm64# dist/arthas-server-all-darwin-amd64# dist/arthas-server-all-darwin-arm64# dist/arthas-server-all-windows-amd64.exe
# Build dev version for current platform only (no frontend)make dev-serverNext Steps
- System Architecture — Understand the overall design
- Configuration Reference — All configurable parameters
- Getting Started — Set up a local development environment