Self-Hosting

Deploy QA Studio on your own server with full control over your data. QA Studio uses SQLite for storage, so there is no external database to configure — just build, run, and persist the data directory.

Prerequisites

Requirement Version Notes
Node.js 20 or higher Required. LTS version recommended for production stability.
pnpm 8 or higher Required for building the project (workspace management).
Google Chrome Latest stable Optional. Only needed if tests use Real Browser mode (useRealChrome: true).
Info

QA Studio uses SQLite as its database, which is embedded in the server process. No external database server (PostgreSQL, MySQL, etc.) is needed. The entire database is a single file in the data directory.

Production Build

Build both the server and the dashboard for production:

bash
# Install dependencies
pnpm install

# Install Playwright browsers
pnpm setup

# Build all packages
pnpm build

This produces two build artifacts:

Package Output Description
Dashboard apps/dashboard/dist/ Static HTML, CSS, and JavaScript files. Can be served by nginx, the Fastify server, or any static file host.
Server apps/server/dist/ Compiled TypeScript to JavaScript. The entry point is apps/server/dist/server.js.

Running in Production

Start the server with the NODE_ENV set to production:

bash
NODE_ENV=production node apps/server/dist/server.js

The server starts on port 3001 by default and listens on all interfaces (0.0.0.0). On startup, it:

  • Initializes (or opens) the SQLite database
  • Runs any pending schema migrations
  • Creates the ./data/ directory if it does not exist
  • Registers all API routes and WebSocket support
  • Starts the automatic cleanup schedule (daily at 3 AM)
  • Loads all enabled scheduled runs via loadAllSchedules()

Environment Variables

QA Studio supports the following environment variables for configuration:

Variable Default Description
PORT 3001 The port the API server listens on.
RETENTION_DAYS 30 Number of days to keep run data before automatic cleanup. Set to 0 to disable cleanup.
NODE_ENV development Set to production for production deployments.

Data Directory

QA Studio stores all persistent data in the ./data/ directory relative to the current working directory. This directory is created automatically on first startup.

Data Directory Structure
data/
├── qa-studio.db        # SQLite database (all projects, tests, runs, etc.)
├── screenshots/        # Screenshot images from test runs
├── videos/             # Video recordings from test runs
└── diffs/              # Visual regression diff images

For data durability, you must persist this directory. If the data/ directory is lost, all projects, tests, run history, screenshots, and visual regression baselines are lost.

Warning

Back up the data/ directory regularly, especially the qa-studio.db file. This single SQLite file contains all your projects, tests, flows, run history, schedules, and baselines. Consider setting up automated daily backups to a remote location.

Backup Strategy

A simple backup approach for the SQLite database:

bash
# Simple file copy (stop writes first for consistency, or use SQLite backup API)
cp data/qa-studio.db backups/qa-studio-$(date +%Y%m%d).db

# Or use SQLite's built-in backup command (safe even while server is running)
sqlite3 data/qa-studio.db ".backup backups/qa-studio-$(date +%Y%m%d).db"

For the screenshots, videos, and diffs directories, you can use rsync or any file backup tool to sync them to a backup location.

PM2 Process Manager

PM2 is a popular process manager for Node.js that provides automatic restarts, log management, and cluster mode. Here is an example configuration:

ecosystem.config.json
{
  "apps": [
    {
      "name": "qa-studio",
      "script": "apps/server/dist/server.js",
      "cwd": "/opt/qa-studio",
      "env": {
        "NODE_ENV": "production",
        "PORT": "3001",
        "RETENTION_DAYS": "30"
      },
      "instances": 1,
      "autorestart": true,
      "watch": false,
      "max_memory_restart": "1G",
      "log_date_format": "YYYY-MM-DD HH:mm:ss"
    }
  ]
}
bash
# Start with PM2
pm2 start ecosystem.config.json

# Save the process list so it restarts after reboot
pm2 save

# Set up PM2 to start on system boot
pm2 startup
Info

QA Studio uses SQLite, which only supports single-writer access. Do not use PM2 cluster mode (multiple instances). Always set instances to 1.

Systemd Service

For Linux servers, you can run QA Studio as a systemd service for automatic start on boot:

/etc/systemd/system/qa-studio.service
[Unit]
Description=QA Studio Test Automation Server
After=network.target

[Service]
Type=simple
User=qa-studio
WorkingDirectory=/opt/qa-studio
ExecStart=/usr/bin/node apps/server/dist/server.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

Environment=NODE_ENV=production
Environment=PORT=3001
Environment=RETENTION_DAYS=30

[Install]
WantedBy=multi-user.target
bash
# Reload systemd to pick up the new service file
sudo systemctl daemon-reload

# Enable the service to start on boot
sudo systemctl enable qa-studio

# Start the service now
sudo systemctl start qa-studio

# Check the status
sudo systemctl status qa-studio

# View logs
sudo journalctl -u qa-studio -f

Nginx Reverse Proxy

In production, you typically run QA Studio behind a reverse proxy like nginx. This lets you serve the dashboard static files efficiently, terminate SSL, and route traffic to the server.

/etc/nginx/sites-available/qa-studio
server {
    listen 80;
    server_name qa-studio.example.com;

    # Serve the dashboard static files
    location / {
        root /opt/qa-studio/apps/dashboard/dist;
        try_files $uri $uri/ /index.html;
    }

    # Proxy API requests to the Fastify server
    location /api/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # SSE support: disable buffering for streaming responses
        proxy_buffering off;
        proxy_cache off;

        # WebSocket support (required for the recorder feature)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Increase timeout for long-running test executions
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # Serve data files (screenshots, videos, diffs)
    location /data/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
    }
}

Key configuration details:

  • Static files: The location / block serves the built dashboard from apps/dashboard/dist. The try_files directive with /index.html fallback ensures client-side routing works correctly.
  • API proxy: All requests to /api/ are forwarded to the Fastify server on port 3001.
  • SSE support: proxy_buffering off is critical for Server-Sent Events (used for live run progress). Without this, nginx buffers the response and the client does not receive real-time updates.
  • WebSocket support: The Upgrade and Connection headers are required for the recorder's WebSocket connection. Without these, the recorder feature will not work.
  • Timeout: Extended to 300 seconds to accommodate long-running test executions.
Important: WebSocket Support

The recorder feature requires WebSocket connections. Make sure your reverse proxy is configured to pass Upgrade and Connection headers to the backend. Without this, the recorder will fail to establish a connection for streaming captured steps.

SSL with Let's Encrypt

For HTTPS, use Certbot to obtain a free SSL certificate:

bash
# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Obtain and install certificate (modifies nginx config automatically)
sudo certbot --nginx -d qa-studio.example.com

# Certbot auto-renews via a systemd timer. Verify:
sudo systemctl status certbot.timer

Serving the Dashboard

There are two approaches to serving the dashboard frontend:

Option 1: Nginx (Recommended)

Serve the static files directly from nginx as shown in the configuration above. This is more efficient because nginx is optimized for static file serving and can handle gzip compression, caching headers, and high concurrency.

Option 2: Fastify Static Plugin

The Fastify server already serves the data/ directory as static files using @fastify/static. You could configure it to also serve the dashboard's dist/ directory. However, using nginx for static files is generally preferred for production deployments.

Deployment Checklist

Before going live, verify the following:

  • Node.js 20+ is installed: node --version
  • All dependencies installed: pnpm install
  • Playwright browsers installed: pnpm setup
  • Production build completed: pnpm build
  • NODE_ENV=production is set
  • PORT is configured (default: 3001)
  • RETENTION_DAYS is set appropriately
  • The data/ directory is on persistent storage
  • A backup strategy is in place for data/qa-studio.db
  • Process manager (PM2 or systemd) is configured for auto-restart
  • Reverse proxy is configured with SSE and WebSocket support
  • Health check endpoint is reachable: curl http://localhost:3001/api/health

Troubleshooting

Issue Solution
Server won't start Check Node.js version (node --version, must be 20+). Check logs with journalctl -u qa-studio or PM2 logs.
Tests fail to run (browser errors) Ensure Playwright browsers are installed: npx playwright install. On Linux, install system dependencies: npx playwright install-deps.
Recorder WebSocket not connecting Verify nginx passes Upgrade and Connection headers. Check that @fastify/websocket is registered.
Live run progress not streaming Ensure proxy_buffering off is set in nginx for the /api/ location block.
Database locked errors Ensure only one server instance is running. Do not use PM2 cluster mode.
Disk space growing Check RETENTION_DAYS value. Trigger manual cleanup: curl -X POST http://localhost:3001/api/admin/cleanup.
Dashboard shows blank page Verify apps/dashboard/dist/ exists and nginx try_files falls back to /index.html.