/ installation
Self-hosted in under 15 minutes.
14 Docker services pulled from Docker Hub — application core + observability stack. One compose file. Your data stays on your infrastructure.
System requirements
Testhide runs 16 containers — backend × 2 replicas, AI inference + training workers, the MCP endpoint, MongoDB and its metrics exporter, Redis, sidecar, plus a full observability stack (Prometheus + Grafana + Tempo + Loki + Alloy). A one-shot config-init container runs on each deploy and then exits. Size your server accordingly.
AI Worker is capped at 8.5 GB and AI API at 4.5 GB. Worker concurrency and memory ceilings are auto-sized from the container budget — no manual tuning needed. On smaller hosts you can still skip YOLO training with AI_YOLO_MIN_ANNOTATIONS=50.
Comfortable headroom for concurrent builds, AI training, observability retention, and log streaming via Alloy/Loki.
AI API and AI Worker share an elastic memory contract via Redis — sum of their mem_limit can exceed physical RAM safely (anti-correlated workloads).
C# Agents (.NET 6)
run on your build hosts and connect to Backend via WebSocket — Windows, Linux, or macOS.
A one-shot config-init container syncs baked observability configs into named volumes on every --force-recreate deploy.
Download docker-compose.yaml
All images are pre-built on Docker Hub — no source code needed. Create a deployment directory and save this file.
View full docker-compose.yaml
expand ↓
# ==========================================
# Testhide — Production Docker Compose (14 services)
# Images: hub.docker.com/u/thuesdays
# Download the full file via the button above.
# ==========================================
name: testhide
services:
# ── Config-init (one-shot, syncs baked configs to named volumes) ─
config-init:
image: thuesdays/testhide-backend:latest
container_name: testhide-config-init
entrypoint: []
command: ["bash", "/app/scripts/sync-configs-to-volumes.sh"]
environment:
- SKIP_LLM_CHECK=true
- LOAD_AI_MODELS=false
- RUN_AI_WORKER=false
- DISABLE_CRON=true
volumes:
- grafana_dashboards:/mnt/grafana_dashboards
- grafana_provisioning:/mnt/grafana_provisioning
- loki_config:/mnt/loki_config
- prometheus_config:/mnt/prometheus_config
- tempo_config:/mnt/tempo_config
- alloy_config:/mnt/alloy_config
restart: "no"
# ── Frontend (Nginx + Angular SPA, TLS termination) ─────────────
frontend:
container_name: testhide_frontend
image: thuesdays/testhide-frontend:latest
restart: unless-stopped
ports: [ "80:80", "443:443", "7771:7771" ]
env_file: [ .env ]
environment:
- CERT_FILE=${CERT_FILE}
- CERT_KEY=${CERT_KEY}
volumes:
- ./ssl:/etc/ssl:ro
- testhide_static_data:/usr/share/nginx/html/static:ro
depends_on: [ backend, ai-api ]
networks: [ testhide-net ]
mem_limit: 256m
# ── Backend (Python API + WebSocket, 2 replicas) ─────────────────
backend:
image: thuesdays/testhide-backend:latest
restart: unless-stopped
env_file: [ .env ]
environment:
- LOAD_AI_MODELS=false
- RUN_AI_WORKER=false
- ENVIRONMENT=production
- SERVICE_NAME=testhide
- BUILD_RCA_RETRAIN_THRESHOLD=9999999 # MUST NEVER train
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- SIDECAR_URL=http://sidecar-docker:8081
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-${PUBLIC_URL}}
volumes:
- testhide_static_data:/app/static
- ./releases:/app/releases
- ./monitoring_scripts:/app/monitoring_scripts
- ./sandbox_data:/app/sandbox_data
- ./ssh_keys:/app/ssh_keys # persists across deploys
- ./docker-compose.yaml:/app/docker-compose.yaml:ro
- testhide_hf_cache:/app/hf_cache
depends_on:
mongo: { condition: service_healthy }
redis: { condition: service_healthy }
sidecar-docker: { condition: service_healthy }
networks: [ testhide-net, testhide-internal ]
mem_limit: 2.5g
deploy:
replicas: 2
resources:
limits: { cpus: "2.0", memory: 2.5g }
# ── AI API (LLM / CLIP / FAISS inference — HTTP only) ────────────
# Elastic memory: 1.5 GB reservation, 4.5 GB limit. Anti-correlated
# with ai-worker via Redis (ai:training:in_progress).
ai-api:
container_name: testhide_ai_api
image: thuesdays/testhide-backend:latest
env_file: [ .env ]
environment:
- LOAD_AI_MODELS=true
- RUN_AI_WORKER=false
- DISABLE_CRON=true
- HF_HOME=/app/hf_cache
- HF_HUB_OFFLINE=${HF_HUB_OFFLINE:-0}
- RC_MODEL_NAME=${RC_MODEL_NAME:-distilbert-base-uncased}
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-${PUBLIC_URL}}
volumes:
- testhide_static_data:/app/static
- testhide_hf_cache:/app/hf_cache
- ./sandbox_data:/app/sandbox_data
- ./releases:/app/releases
depends_on:
mongo: { condition: service_healthy }
redis: { condition: service_healthy }
networks: [ testhide-net ]
mem_limit: 4.5g
memswap_limit: 4.5g
mem_reservation: 1500m
cpus: 2.5
# ── AI Worker (training, vectorization, Edge AI) ─────────────────
# Elastic memory: 5 GB reservation, 8.5 GB limit.
ai-worker:
container_name: testhide_ai_worker
image: thuesdays/testhide-backend:latest
env_file: [ .env ]
environment:
- LOAD_AI_MODELS=true
- RUN_AI_WORKER=true
- RUN_INFERENCE_WORKER=true # drains /ai/inference queue
- DISABLE_CRON=true
- SKIP_BUILD_RECOVERY=true
# Memory ceiling, dataset/batch caps and CPU/GPU selection are auto-sized
# from the container's RAM + CPU/GPU — no AI tuning env vars needed.
- AI_LLM_CTX=${AI_LLM_CTX:-8192}
- AI_TRAINING_LOCK_TTL_SEC=${AI_TRAINING_LOCK_TTL_SEC:-3600}
- AI_VECTORS_SIDECAR_ENABLED=${AI_VECTORS_SIDECAR_ENABLED:-1}
- HF_HOME=/app/hf_cache
- RC_MODEL_NAME=${RC_MODEL_NAME:-distilbert-base-uncased}
volumes:
- testhide_static_data:/app/static
- testhide_hf_cache:/app/hf_cache
- ./sandbox_data:/app/sandbox_data
- ./releases:/app/releases
depends_on:
mongo: { condition: service_healthy }
redis: { condition: service_healthy }
networks: [ testhide-net ]
mem_limit: 8.5g
memswap_limit: 8.5g
mem_reservation: 5g
cpus: 5.0
# ── MongoDB 8 ──────────────────────────────────────────────────
mongo:
container_name: testhide_mongo
image: mongo:8.2.3
restart: unless-stopped
command: ["mongod","--auth","--wiredTigerCacheSizeGB","2"]
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASS}
ports: [ "27017:27017" ]
volumes: [ "${MONGO_DATA_PATH}:/data/db" ]
networks: [ testhide-net ]
healthcheck:
# Unauthenticated ping (allowed before auth), with a client-free fallback:
# the app user does NOT live in `admin`, and `mongosh` is not on PATH in mongo:8.2.3.
test: ["CMD-SHELL","mongosh --quiet --eval \"db.adminCommand('ping').ok\" 2>/dev/null || bash -c 'exec 3<>/dev/tcp/127.0.0.1/27017' 2>/dev/null"]
interval: 30s · timeout: 5s · retries: 5 · start_period: 40s
mem_limit: 3g
# ── Redis 7 ───────────────────────────────────────────────────
redis:
container_name: testhide_redis
image: redis:7-alpine
command: ["redis-server","--maxmemory","512mb","--maxmemory-policy","allkeys-lru","--appendonly","no","--requirepass","${REDIS_PASSWORD}"]
networks: [ testhide-net ]
mem_limit: 768m
# ── Redis Insight (optional admin UI, proxied via nginx) ───────
redisinsight:
container_name: testhide_redisinsight
image: redis/redisinsight:3.4.2 # pinned — :latest 404s on Docker Hub
depends_on:
redis: { condition: service_healthy }
networks: [ testhide-net ]
mem_limit: 256m
# ── Docker Socket Sidecar (SEC-004) ────────────────────────────
# Sidecar token auto-derived from JWT_SECRET via HMAC-SHA256.
sidecar-docker:
container_name: testhide_sidecar_docker
image: thuesdays/testhide-sidecar:latest
environment:
- JWT_SECRET=${JWT_SECRET}
- SIDECAR_ALLOWED_IMAGES=${SIDECAR_ALLOWED_IMAGES}
- SIDECAR_ALLOWED_EXEC_PATTERNS=${SIDECAR_ALLOWED_EXEC_PATTERNS}
- SIDECAR_ALLOWED_NETWORKS=${SIDECAR_ALLOWED_NETWORKS}
- SIDECAR_PORT=8081
volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
networks: [ testhide-internal ]
mem_limit: 128m
healthcheck:
test: ["CMD","wget","-qO-","http://localhost:8081/health"]
# ── Observability: Prometheus (metrics) ────────────────────────
# Loopback bind by default. Override via PROMETHEUS_PORT_BIND.
prometheus:
image: prom/prometheus:v2.51.0
container_name: testhide-prometheus
depends_on:
config-init: { condition: service_completed_successfully }
volumes:
- prometheus_config:/etc/prometheus:ro
- prometheus_data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d
- --web.enable-lifecycle
- --web.enable-remote-write-receiver
ports: [ "${PROMETHEUS_PORT_BIND:-127.0.0.1:9090}:9090" ]
networks: [ testhide-net ]
mem_limit: 512m
# ── Observability: Grafana (HTTPS, dashboards) ─────────────────
grafana:
image: grafana/grafana:10.4.2
container_name: testhide-grafana
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?Set in .env}
GF_SERVER_ROOT_URL: ${GRAFANA_ROOT_URL:-${PUBLIC_URL}:3000}
GF_SERVER_PROTOCOL: https
GF_SERVER_CERT_FILE: /etc/grafana/ssl/server.crt
GF_SERVER_CERT_KEY: /etc/grafana/ssl/server.key
GF_FEATURE_TOGGLES_ENABLE: traceqlEditor
GF_AUTH_ANONYMOUS_ENABLED: "false"
volumes:
- grafana_provisioning:/etc/grafana/provisioning:ro
- grafana_dashboards:/var/lib/grafana/dashboards:ro
- grafana_data:/var/lib/grafana
- ${GRAFANA_CERT_HOST_CRT:-./ssl/testhide.crt}:/etc/grafana/ssl/server.crt:ro
- ${GRAFANA_CERT_HOST_KEY:-./ssl/testhide.key}:/etc/grafana/ssl/server.key:ro
ports: [ "3000:3000" ]
depends_on:
config-init: { condition: service_completed_successfully }
prometheus: { condition: service_started }
tempo: { condition: service_started }
loki: { condition: service_started }
networks: [ testhide-net ]
mem_limit: 256m
# ── Observability: Tempo (distributed traces, OTel) ────────────
tempo:
image: grafana/tempo:2.4.1
container_name: testhide-tempo
command: ["-config.file=/etc/tempo/tempo.yml"]
depends_on:
config-init: { condition: service_completed_successfully }
volumes:
- tempo_config:/etc/tempo:ro
- tempo_data:/tmp/tempo
ports: [ "4317:4317", "4318:4318", "3200:3200" ]
networks: [ testhide-net ]
mem_limit: 512m
# ── Observability: Loki (log storage) ──────────────────────────
loki:
image: grafana/loki:2.9.4
container_name: testhide-loki
command: -config.file=/etc/loki/loki.yml
depends_on:
config-init: { condition: service_completed_successfully }
volumes:
- loki_config:/etc/loki:ro
- loki_data:/loki
ports: [ "3100:3100" ]
networks: [ testhide-net ]
mem_limit: 256m
# ── Observability: Grafana Alloy (log/metric collector) ────────
alloy:
image: grafana/alloy:v1.5.1
container_name: testhide-alloy
volumes:
- alloy_config:/etc/alloy:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- testhide_static_data:/var/log/testhide:ro
ports: [ "12345:12345" ]
depends_on:
config-init: { condition: service_completed_successfully }
loki: { condition: service_started }
prometheus: { condition: service_started }
networks: [ testhide-net ]
mem_limit: 256m
volumes:
testhide_static_data: { name: testhide_static_data }
testhide_hf_cache: { name: testhide_hf_cache }
prometheus_data: { name: testhide_prometheus_data }
grafana_data: { name: testhide_grafana_data }
tempo_data: { name: testhide_tempo_data }
loki_data: { name: testhide_loki_data }
# Config volumes — populated by config-init from the image.
grafana_dashboards: { name: testhide_grafana_dashboards }
grafana_provisioning: { name: testhide_grafana_provisioning }
loki_config: { name: testhide_loki_config }
prometheus_config: { name: testhide_prometheus_config }
tempo_config: { name: testhide_tempo_config }
alloy_config: { name: testhide_alloy_config }
networks:
testhide-net: { name: testhide-net }
testhide-internal: { driver: bridge, internal: true }
Configure .env
Edit .env — every variable is listed below. REQUIRED must be set before first boot. OPTIONAL have working defaults.
CORS_ALLOWED_ORIGINS, GRAFANA_ROOT_URL, Angular bundles.PUBLIC_URL. Add staging/dev origins if needed. Previous wildcard default * raised a startup security warning.PRODUCTION
DEBUG
PRODUCTION=true
DEBUG=false
ENVIRONMENT=production in all prod/staging deployments — SEC-002 refuses startup with a default JWT_SECRET in those modes. Keep DEBUG=false for security headers and to disable stacktrace responses.1 so client IPs are read from X-Forwarded-For (rate limits, ban store, IP allowlists work correctly). Default 0 uses the TCP peer.INTERNAL_WS_URL
PORT
INTERNAL_WS_URL=ws://backend:8080
PORT=8080
python3 -c "import secrets; print(secrets.token_hex(32))"CERT_KEY
CERT_KEY=/etc/ssl/testhide.key
./ssl/ on the host — that directory is bind-mounted to /etc/ssl.MONGO_PASS
MONGO_PASS=your_strong_password
MONGO_PORT
MONGO_DB_NAME
MONGO_AUTH_SOURCE
MONGO_PORT=27017
MONGO_DB_NAME=testhide_database
MONGO_AUTH_SOURCE=testhide_database
REDIS_PORT
REDIS_DB
REDIS_PORT=6379
REDIS_DB=0
(2 × CPU cores) + 1. Backend runs 2 replicas — total processes = replicas × workers.GUNICORN_GRACEFUL_TIMEOUT
GUNICORN_KEEPALIVE
GUNICORN_LOG_LEVEL
GUNICORN_GRACEFUL_TIMEOUT=30
GUNICORN_KEEPALIVE=5
GUNICORN_LOG_LEVEL=info
GUNICORN_TIMEOUT if you see 502 errors during large report uploads or long AI inference calls.AI_LLM_MODEL
AI_LLM_MODEL=Phi-3.5-mini-instruct-Q5_K_M.gguf
Q4_K_M to save ~800 MB on 32 GB servers. AI_LLM_MODEL is auto-picked when only one GGUF is present.AI_LLM_CTX to 8192 on 32 GB servers to cut peak RAM by ~4 GB. Inference threads, GPU-layer offload and CPU/GPU selection are auto-sized from the host — the former AI_LLM_THREADS, AI_LLM_GPU_LAYERS and AI_FORCE_CPU are no longer required.ai-api and ai-worker — they share the testhide_hf_cache volume.TRANSFORMERS_OFFLINE
TRANSFORMERS_OFFLINE=0
1 after a successful first boot for air-gapped mode. Models persist in the testhide_hf_cache Docker volume between restarts.AI_MEMORY_LIMIT_MB is no longer required.8192 saves ~1.5 GB KV-cache vs 32768 — recommended on 32 GB hosts. (The pandas merge batch size is now auto-sized from the container’s RAM budget — the former AI_MERGE_BATCH_SIZE is no longer required.)AI_RETRAIN_QUEUE_BUSY_THRESHOLD was removed — retraining is gated on real memory headroom now, not on a queue-backlog count, and nothing reads the name.ai-worker; without it, /ai/inference/{id} result-polling never resolves. Do not unset.MKL_NUM_THREADS
OPENBLAS_NUM_THREADS
NUMEXPR_NUM_THREADS
EDGE_AI_TIMEOUT_SEC
EDGE_LLM_MODEL
EDGE_EMBEDDER_MODEL
EDGE_AI_TIMEOUT_SEC=600
EDGE_LLM_MODEL=Phi-3.5-mini-instruct-Q5_K_M.gguf
EDGE_EMBEDDER_MODEL=minilm-l6-v2.onnx
AI_LLM_MODEL.AI_DATASET_SHARD_ROWS, AI_DATASET_MAX_ROWS, AI_DATASET_MAX_MB, AI_COMPACT_AFTER_SHARDS and AI_BUDGET_* are no longer required.text_vector/image_vector from Parquet rows into raw float32 sidecar files. −60% dataset size, ×3-10 training speedup. On first deploy, migrate_vectors_to_sidecar.py runs automatically (one-shot, idempotent). Safety invariant (dataset_signature hash) blocks reads if shards diverge from sidecar. Set =0 to disable.shadow — predictions persisted to db_ai_diagnostics but not shown (safe default). active — ML label used when confidence ≥ 0.6, replacing the regex baseline. disabled — model not loaded. Auto-promotes from shadow to active when shadow cron observes ≥ 70% agreement with regex baseline over 7 days and n ≥ 50 samples.db_rca_corpus before auto-retraining fires. Backend service overrides this to 9999999 in compose — training MUST run only in ai-worker (RUN_AI_WORKER=true).LOG_LEVEL
LOG_LEVEL=INFO
trace_id/span_id so Loki + Tempo derive trace links automatically. Set LOG_LEVEL=DEBUG for verbose output (testhide loggers only — does not affect external libs)./api/v3/metrics in Prometheus exposition format. 12 baseline metrics (HTTP rps + latency, build events, WS connections, LLM cost/tokens, AI precompute duration). Scraped every 30s by Prometheus.OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_SAMPLE_RATE
SERVICE_NAME
OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4318
OTEL_SAMPLE_RATE=0.1
SERVICE_NAME=testhide
1.0 = 100% (dev), 0.1 = 10% (prod recommendation).python3 -c "import secrets; print(secrets.token_urlsafe(32))"${PUBLIC_URL}:3000 if unset.GRAFANA_CERT_HOST_KEY
GRAFANA_CERT_HOST_KEY=./ssl/testhide.key
docker-compose.yaml). By default reuses the main testhide cert. Override only if Grafana needs a separate certificate.0.0.0.0:9090 in production./api/v3/db/dump. Fail-closed — leave empty and the endpoint denies every request with HTTP 403.1 if you have a documented reason (self-signed internal webhooks, on-prem MS Teams behind a private CA). Even then, the individual webhook row must also set allow_insecure_tls=True — both gates must agree before TLS verification is skipped.AI_YOLO_FORCE_CPU is no longer needed. yolov8n.pt is the nano model: fast, low memory. Use yolov8s.pt for better accuracy at the cost of ~2× RAM.AI_YOLO_FREEZE
AI_YOLO_MIN_ANNOTATIONS
AI_YOLO_WEIGHT_CORRECTION
AI_YOLO_WEIGHT_MANUAL
AI_YOLO_FREEZE=10
AI_YOLO_MIN_ANNOTATIONS=10
AI_YOLO_WEIGHT_CORRECTION=10
AI_YOLO_WEIGHT_MANUAL=5
AI_YOLO_MIN_ANNOTATIONS to skip training on small datasets. On 32 GB servers set to 50 to avoid OOM during training runs.LDAP_CA_BUNDLE.1.41 on the backend; override only for an older/newer engine.SSL_CERT_FILE
REQUESTS_CA_BUNDLE=/etc/ssl/ldap-ca.pem
SSL_CERT_FILE=/etc/ssl/ldap-ca.pem
SIDECAR_ALLOWED_NETWORKS
SIDECAR_ALLOWED_NETWORKS=testhide-internal,bridge,testhide-net
JIRA_USERNAME
JIRA_PASSWORD
BITBUCKET_API_VERSION
JIRA_USERNAME=testhide-service
JIRA_PASSWORD=api_token
BITBUCKET_API_VERSION=1.0
LDAP_PORT
LDAP_CA_BUNDLE
LDAP_TLS_INSECURE
LDAP_PORT=636
LDAP_CA_BUNDLE=/etc/ssl/ldap-ca.pem
LDAP_TLS_INSECURE=0
./ssl/ldap-ca.pem on the host. Set LDAP_TLS_INSECURE=1 only for local dev — never in production.Place SSL certificates
Create a ./ssl/ directory next to docker-compose.yaml — it's bind-mounted read-only into Nginx.
-rw------- ssl/testhide.key # private key (chmod 600)
-rw-r--r-- ssl/ldap-ca.pem # optional: LDAP / proxy CA bundle
-keyout ssl/testhide.key -out ssl/testhide.crt \
-subj "/CN=testhide.local" && chmod 600 ssl/testhide.key
Launch the stack
First run downloads ~4–6 GB of images and AI models. Allow 10–15 minutes.
✔ Container testhide-config-init Exited (0) — configs synced
✔ Container testhide_mongo Healthy
✔ Container testhide_redis Healthy
✔ Container testhide_redisinsight Started
✔ Container testhide_sidecar_docker Healthy
✔ Container testhide_backend Started (×2 replicas)
✔ Container testhide_ai_api Started
✔ Container testhide_ai_worker Started
✔ Container testhide_frontend Started
✔ Container testhide-prometheus Started
✔ Container testhide-tempo Started
✔ Container testhide-loki Started
✔ Container testhide-grafana Started
✔ Container testhide-alloy Started
Connect build agents
A lightweight launcher wraps the agent binary, managing service lifecycle and auto-updates. Agents connect via WebSocket. Available for Windows, Linux, macOS, and Docker.
./testhide
./testhide --daemon
Auto-start on boot · runs as SYSTEM · no login required
TesthideAgent. Manage via services.msc or net stop TesthideAgent / sc.exe delete TesthideAgent.C:\ProgramData\Testhide\config.json. Verify with .\testhide.exe config --show.sudo tee /etc/apt/sources.list.d/testhide.list
Restarts automatically on crash · logs via journalctl
sudo apt-get upgrade testhide.journalctl -u testhide -f · Status: systemctl status testhide[testhide]
name=Testhide Agent
baseurl=https://dl.testhide.com/rpm
enabled=1
gpgcheck=0
EOF
sudo dnf upgrade testhide.journalctl -u testhide -f · Status: systemctl status testhide-o /opt/testhide/testhide && sudo chmod +x /opt/testhide/testhide
--url wss://YOUR_DOMAIN:7771 \
--license-key YOUR_LICENSE_KEY
[Unit]
Description=Testhide CI/CD Agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=TESTHIDE_SERVICE_MODE=1
ExecStart=/opt/testhide/testhide --daemon
WorkingDirectory=/opt/testhide
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
~/.testhide/client/{version}/ from dl.testhide.com./root/.testhide/config.json when running as root.&& curl -fsSL https://dl.testhide.com/stable/testhide-macos-$ARCH \
-o testhide && chmod +x testhide
xattr -d com.apple.quarantine ./testhide or allow via System Settings → Privacy & Security.testhide-macos-x64 (Intel) · testhide-macos-arm64 (M1/M2/M3)--name testhide-agent \
--restart unless-stopped \
-e TESTHIDE_SERVICE_MODE=1 \
-e TESTHIDE_NODE_TYPE=dynamic \
thuesdays/testhide-agent:latest
--url wss://YOUR_DOMAIN:7771 \
--license-key YOUR_LICENSE_KEY
thuesdays/testhide-agent:latest · Base: Python 3.12 slim · Includes SSH server for backend remote-terminal access during test runs.-v /var/run/docker.sock:/var/run/docker.sock only if your tests need to spawn Docker containers (Docker-in-Docker).ws:// or wss://. Must match WS_URL in your server .env.uninstall-service
.\testhide.exe uninstall-service
TesthideAgent Windows Service. Requires Administrator. The launcher installs itself as a service, auto-starts on boot.TESTHIDE_SERVICE_MODE=1 internally. Used in the systemd ExecStart line — do not use for interactive sessions.dl.testhide.com. The launcher rolls back to the previous version on repeated crashes and re-downloads if needed.Verify & health-check
Run these before connecting your first pipeline.
http_requests_total{method="GET",route="/api/v3/health",status="200"} 12
...
Upgrades & operations
-u $MONGO_USER -p $MONGO_PASS \
--authenticationDatabase admin \
--out /data/db/backup_$(date +%Y%m%d)
Rather skip the infrastructure? We run it for you.
Cloud Starter ($49/mo) — managed instance, 3 concurrent builds, 30-day retention, full 8-model dashboard.