This guide helps you to set up self-hosted TestDriver instances on AWS. At the end, you have automated test infrastructure. It starts and stops instances when you need them.
How It Works
TestDriver manages the AWS EC2 instances for your tests automatically:
- Deploy CloudFormation. Set up the infrastructure one time.
- Configure Vitest. Add one line to your config.
- Run Tests. The instances start automatically, run the tests, and stop.
That is all. You do not need to manage the instances by hand.
Quickstart
Overview
The setup process is simple:
- Deploy CloudFormation — Creates VPC, security groups, IAM roles, and launch templates
- Configure Vitest — Add
setup-awsto automatically manage instance lifecycle - Run Tests — Set
TD_OS=windowswith AWS credentials and instances spawn/terminate automatically
Prerequisites
Before you begin, ensure you have:
- AWS account with CloudFormation permissions
- AWS CLI installed and configured (
aws configure) - Access to the TestDriver AMI — Contact us with your AWS region
- A GitHub repository for your tests
Step 1: Deploy CloudFormation Stack
Our CloudFormation template creates all the AWS infrastructure you need:
- Dedicated VPC with public subnet
- Security group with required port access
- IAM roles and instance profiles
- EC2 launch template for instance creation
<Card title="Launch Stack" icon="aws" href="https://console.aws.amazon.com/cloudformation/home#/stacks/create/review?templateURL=https://v7-cloudformation-template.s3.us-east-2.amazonaws.com/cloudformation.yaml" horizontal arrow > Deploy TestDriver infrastructure with one click </Card> Configure the stack parameters: - **Stack name**: `testdriver-infrastructure` (or your preferred name) - **ProjectTag**: `testdriver` - **AllowedIngressCidr**: Your IP range (e.g., `203.0.113.0/24`) - **InstanceType**: `c5.xlarge` (recommended) - **CreateKeyPair**: `true` <Warning> **Security**: Replace `AllowedIngressCidr` with your specific IP ranges to restrict VPC access. Avoid using `0.0.0.0/0` in production. </Warning> ### Get Your Launch Template ID After the stack creation completes, navigate to the **Outputs** tab to find your `LaunchTemplateId`:  <Tip> **Save this ID** — you'll need it for spawning instances and CI configuration. </Tip>
```bash aws cloudformation deploy \ --template-file setup/aws/cloudformation.yaml \ --stack-name testdriver-infrastructure \ --parameter-overrides \ ProjectTag=testdriver \ AllowedIngressCidr=0.0.0.0/0 \ InstanceType=c5.xlarge \ CreateKeyPair=true \ --capabilities CAPABILITY_IAM ``` <Warning> **Security**: Replace `AllowedIngressCidr=0.0.0.0/0` with your specific IP ranges to restrict VPC access. </Warning> ### Get Your Launch Template ID After deployment completes, retrieve the launch template ID: ```bash aws cloudformation describe-stacks \ --stack-name testdriver-infrastructure \ --query 'Stacks[0].Outputs[?OutputKey==`LaunchTemplateId`].OutputValue' \ --output text ``` <Tip> **Save this ID** — you'll need it for spawning instances and CI configuration. </Tip>
Step 2: Configure Vitest
Add the AWS setup hook to your vitest.config.mjs:
javascriptimport { defineConfig } from 'vitest/config'; import { config } from 'dotenv'; import TestDriver from 'testdriverai/vitest'; config(); // Load .env file export default defineConfig({ test: { testTimeout: 900000, hookTimeout: 900000, maxConcurrency: 3, reporters: [ 'default', TestDriver(), ['junit', { outputFile: 'test-report.junit.xml' }] ], setupFiles: ['testdriverai/vitest/setup', 'testdriverai/vitest/setup-aws'], }, });
Step 3: Write Your Tests
Tests should use context.ip || process.env.TD_IP for the IP configuration:
javascriptimport { describe, it } from "vitest"; import { TestDriver } from "testdriverai/vitest/hooks"; describe("My Test", () => { it("should run on self-hosted instance", async (context) => { const testdriver = TestDriver(context, { ip: context.ip || process.env.TD_IP, }); await testdriver.provision.chrome({ url: "https://example.com" }); // ... your test steps }); });
Step 4: Run Tests
Locally
bashTD_OS=windows \ AWS_REGION=us-east-2 \ AWS_LAUNCH_TEMPLATE_ID=lt-xxx \ AMI_ID=ami-0504bf50fad62f312 \ vitest run
In GitHub Actions
Automate testing with self-hosted instances in your CI/CD pipeline. TestDriver automatically spawns a fresh instance for each test, runs the test, and terminates the instance.
yamlname: TestDriver Self-Hosted Windows Tests on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Run Windows tests with self-hosted instances run: npx vitest run examples/*.test.mjs env: TD_API_KEY: ${{ secrets.TD_API_KEY }} TD_OS: windows AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-2 AWS_LAUNCH_TEMPLATE_ID: ${{ secrets.AWS_LAUNCH_TEMPLATE_ID }} AMI_ID: ${{ secrets.AMI_ID }} - name: Upload test results if: always() uses: actions/upload-artifact@v4 with: name: test-results path: test-report.junit.xml
Required GitHub Secrets
| Secret | Description | Example |
|---|---|---|
AWS_ACCESS_KEY_ID | AWS access key | AKIAIOSFODNN7EXAMPLE |
AWS_SECRET_ACCESS_KEY | AWS secret key | wJalrXUtnFEMI/K7MDENG... |
AWS_REGION | AWS region | us-east-2 |
AWS_LAUNCH_TEMPLATE_ID | From CloudFormation output | lt-07c53ce8349b958d1 |
AMI_ID | TestDriver AMI ID | ami-0504bf50fad62f312 |
TD_API_KEY | Your TestDriver API key | From console.testdriver.ai |
Example Workflows
For complete production examples, see:
- Scheduled Tests — Daily automated test runs
- Reusable Workflow — Shared configuration for PR and scheduled tests
Advanced Usage
Using an Existing Instance
If you already have a running instance, you can skip automatic spawning by providing TD_IP:
bashTD_OS=windows TD_IP=1.2.3.4 vitest run
The setup-aws hook will detect TD_IP is already set and skip spawning a new instance.
Manual Instance Management
For advanced use cases, you can manually spawn instances using the spawn-runner.sh script:
bashAWS_REGION=us-east-2 \ AMI_ID=ami-0504bf50fad62f312 \ AWS_LAUNCH_TEMPLATE_ID=lt-xxx \ bash setup/aws/spawn-runner.sh
Output:
PUBLIC_IP=1.2.3.4 INSTANCE_ID=i-1234567890abcdef0 AWS_REGION=us-east-2
Then manually terminate when done:
bashaws ec2 terminate-instances \ --instance-ids i-1234567890abcdef0 \ --region us-east-2
For complete production examples, see:
- Scheduled Tests — Daily automated test runs
- Reusable Workflow — Shared configuration for PR and scheduled tests
Connecting to an Instance
You can connect to running instances via:
- RDP — Use the public IP on port 3389
- VNC — Access via web browser at
http://<public-ip>:5900 - AWS Console — Use EC2 Instance Connect or Session Manager
AMI Customization
The TestDriver Golden Image comes pre-configured with:
- Windows Server with desktop environment
- VNC + web server for remote access
- Python, Node.js, Git
- TestDriver agent and dependencies
Creating a Custom AMI
You can customize the AMI to include additional software or configurations:
Security Best Practices
Network Security
- Restrict CIDR blocks — Only allow access from known IP ranges
- Use VPC endpoints — For private AWS service communication
- Enable VPC Flow Logs — For network monitoring
AWS Authentication
Use OIDC instead of long-term credentials for GitHub Actions:
yamlpermissions: id-token: write contents: read steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole aws-region: us-east-2
See GitHub's OIDC documentation for setup instructions.
Instance Security
- Terminate immediately after tests complete
- Monitor costs with AWS billing alerts
- Use least-privilege IAM roles
- Enable CloudTrail for audit logging
Troubleshooting
Instance Not Responding
- Check security groups — Verify required ports are open (RDP 3389, VNC 5900, TestDriver ports)
- Verify status checks — Ensure instance has passed AWS status checks
- Check AMI compatibility — Some instance types don't support certain AMIs
Connection Timeouts
- Verify network connectivity from CI runner to instance
- Check VPC routing and internet gateway configuration
- Confirm instance is in the correct subnet
AWS CLI Errors
- Validate credentials and permissions
- Check service quotas and limits
- Verify region consistency across all resources


