Overview

My gaming rig died. Gaming rigs can be expensive ($1000-$4000 on average, depending on how much you’re willing to spend). So I spun up a GPU instance on AWS to test how Doom 2016 played as a proof of concept. After the first 5-10 minutes of letting the hard drive warm up, it was buttery smooth. This lead me to investigate the overall costs for using AWS for my PC gaming rather than buying a new rig.

After doing the math, I opted to go the AWS gaming route. It’s not as staightforward as booting up a normal gaming rig. And the actual cost can vary wildly depending on how much time I spend gaming. But for my casual gaming purposes (under 20 hours of PC gaming per month), it fits my needs perfectly fine for a fraction of the cost.

AWS Gaming Rig Cost Estimates

Here are the numbers. All calculations were done using pricing on 2020-03-20 for us-west-2b. I use g4dn.xlarge for my gaming and it runs perfectly fine for my tastes, but for those who want more power on their rigs, I provide the numbers below for the more powerful g4dn offerings.

Type CPUs RAM (GB) GPUs SSD (GB) on demand/hr spot/hr 20 hr spot/mo/yr
g4dn.xlarge 4 16 1 125 $0.5260 $0.1704 $40.896
g4dn.2xlarge 8 32 1 225 $0.7520 $0.2378 $57.072
g4dn.4xlarge 16 64 1 225 $1.2040 $0.3612 $86.688
g4dn.8xlarge 32 128 1 1x900 $2.1760 $0.6528 $156.672
g4dn.16xlarge 64 256 1 1x900 $4.3520 $1.3056 $313.344

* Source: AWS Console Spot Price History for us-west-2b on 2020-03-20. Yearly calculations were done with an estimate of 20 hours of gaming per month throughout a year.

Item Price
General Purpose SSD (gp2) Volumes $0.10 per GB-month of provisioned storage
EBS Snapshots $0.05 per GB-month of data stored
150 GB EBS Snapshots / mo $7.50
150 GB EBS gp2 Volume for entire month $15.00
150 GB EBS gp2 Volume for 20 hours / mo ($15.00 / 730) * 20 = $0.41
Total storage cost for 20 hours usage / mo $7.91 / month
Total storage cost for 20 hours usage / mo / yr $94.92 / year

* Sources: https://aws.amazon.com/ebs/pricing/ and https://calculator.s3.amazonaws.com/

Item EBS EC2 instance Total/year
g4dn.xlarge + EBS costs $94.92 $40.896 $135.816
g4dn.2xlarge + EBS costs $94.92 $57.072 $151.992
g4dn.4xlarge + EBS costs $94.92 $86.688 $181.608
g4dn.8xlarge + EBS costs $94.92 $156.672 $251.592
g4dn.16xlarge + EBS costs $94.92 $313.344 $408.264

One really important thing to note is that while the average spot price might be a certain amount, if you set your max spot price threshold to be the on-demand price, you will likely end up paying more than the average spot price. For example, I used this approach and my spend was twice the average spot price according to the following screen:

spot price savings

With that said, even if I paid double for the instance, that would still only come out to about $80 per year rather than $40 (plus EBS costs), which is a negligible difference compared to the price of a decent gaming rig.

How to make a new image

AMI and instance settings

Configuration script

I use Windows for my gaming rig and I run this Powershell code to prepare certain parts of the system.

# Clean up desktop.
cd ~/Desktop
rm *

# Create disconnect script on Desktop. This is used to leave the login session
# active, while disconnecting from the screen. When you use this script, the
# RDP client will likely throw an error, but your session should still be
# active on the EC2 instance.
$dcData = @"
`$sid = (Get-Process -PID `$pid).SessionID
tscon `$sid /dest:console
"@
$dcData | Out-File -FilePath ~/Desktop/dc.ps1 -Encoding ASCII

# Enable audio services.
Get-Service | Where {$_.Name -match "audio"} | Start-Service
Get-Service | Where {$_.Name -match "audio"} | Set-Service -StartupType "Automatic"

# Set up chocolatey.
Set-ExecutionPolicy Bypass -Scope Process -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

# Install Steam.
choco install steam -yes

# Install autologon.
# https://docs.microsoft.com/en-us/sysinternals/downloads/autologon
choco install autologon -yes

# Set new Windows password.
$Password = Read-Host "Enter the new password" -AsSecureString
$UserAccount = Get-LocalUser -Name "Administrator"
$UserAccount | Set-LocalUser -Password $Password

# Run autologon.
AutoLogon.exe

# Log into Steam.
& 'C:\Program Files (x86)\Steam\Steam.exe'

Configure steam’s host stream settings

This makes it so that graphics run smoothly during streaming. Graphics will be a bit choppy for the first few minutes after you spin up a new instance, but it should even out shortly after that.

Steam -> Settings -> Remote Play -> Advanced Host Options

advanced host options

Enable Steam Beta (optional)

I don’t know if this makes things better or worse, but a few sources online said that the beta tends to have some benefits over the stable client. I haven’t done extensive testing either way. The beta seems to work for me. Your mileage may vary.

Steam Menu -> Settings -> Beta Participation -> Change

settings change

Disable friends list (optional)

I don’t plan on chatting with friends on a gaming rig that’s costing me by the hour.

forever alone dot jay peg

Install games

Whatever games you want.

Launch at least one game and install required drivers

The next step is to make sure the games are playable without dealing with driver/software installation issues.

  1. Launch every game you installed. This may result in an attempt to automatically install some drivers or additional software, such as the .NET Framework.
  2. Reboot the system from the OS level as opposed to restarting from the AWS console.
  3. Wait up to 10 minutes to verify that both the account got logged onto and that Steam autostarted (this is a default setting of Steam). Alternatively, you can log in and make sure Steam starts. Then you can use the disconnect script that you created using the code shown above for dc.ps1. This will make it so the system stays logged into the Desktop UI while your RDP session is disconnected.
  4. After you have successfully logged into Steam on the AWS system, you should see a notification on your local system (assuming you’re logged into Steam on another system) that indicates the AWS system is ready for streaming.
    available for streaming
    Note: if the system successfully auto-logged on to Steam, it will possibly only last for a little bit depending on the whims of Steam, and then will stop working. So you’ll likely need to log into steam manually each time you spin your gaming rig up after a while.
  5. Try to launch a game from your streaming client now that a reboot has occurred and make sure the audio works over the stream. This is important to do since for some reason, some games work for me the first time prior to a reboot, but then won’t work unless I’ve enabled the audio service (see script above).

Shut down and make an image

Shut down the system from the OS (not the AWS console) and make an AMI based off the stopped instance. This will take upwards of 30-60 minutes to create depending on how much hard drive space you used when installing games. I prepend my snapshots with gaming- so I can use Terraform to find them and add them to launch templates.

create image

Deployment

I made a Terraform module to launch the instances. It is composed of two files: gaming.tf and variables.tf. Here is the code combined into one snippet:

data "aws_ami" "gaming" {
  most_recent = true
  owners      = ["self"]

  filter {
    name   = "name"
    values = ["gaming-*"]
  }
}

# I intentionally do not set the spot_price value because it defaults to the
# on-demand price. Based on my experience, this means I'm paying more than the
# average spot price. However, unless spot prices increase beyond the on-demand
# price, this will always be fulfilled.
resource "aws_spot_instance_request" "gaming" {
  ami           = data.aws_ami.gaming.id
  ebs_optimized = "false"
  instance_type = "g4dn.xlarge"
  key_name      = "primary"
  subnet_id     = var.subnet_id

  # Wait until the spot request is fulfilled. This should be fairly quick, so
  # if it takes a long time (minutes), then it should indicate a potential spot
  # instance shortage.
  wait_for_fulfillment = true

  # This allows me to take a snapshot of the instance and delete the
  # provisioned volume.
  instance_initiated_shutdown_behavior = "stop"

  # Do not request another instance fulfilment after this instance is
  # terminated.
  spot_type = "one-time"

  vpc_security_group_ids = [
    var.gaming_sg_id,
    var.egress_sg_id,
  ]

  tags = {
    Name = "gaming"
  }
}

resource "aws_eip_association" "gaming_eip_allocation_id" {
  instance_id   = aws_spot_instance_request.gaming.spot_instance_id
  allocation_id = var.gaming_eip_allocation_id
}

variable "subnet_id" {
  type = string
}

variable "gaming_sg_id" {
  type = string
}

variable "egress_sg_id" {
  type = string
}

variable "gaming_eip_allocation_id" {
  type = string
}

Enjoy!