Running multiplayer game servers around the clock is one of the fastest ways to burn through a cloud budget. A fleet sized for Friday-night peak traffic sits half-empty on a Tuesday morning, and every idle instance still bills at full On-Demand price. Amazon’s own pricing pages put the fix in plain numbers: Amazon GameLift Servers advertises Spot Instance savings of 50% to 85% compared with On-Demand pricing, and plain EC2 Spot Instances can run up to 90% cheaper than On-Demand rates. This tutorial walks through building a cost-aware, autoscaling game server fleet on AWS using GameLift FleetIQ, EC2 Spot Fleets, and Auto Scaling groups, with the cost-control guardrails needed to keep interruptions from ruining a match.
By the end of this guide you will have a working game server group that mixes Spot and On-Demand capacity, scales automatically with player demand, and handles Spot interruption notices gracefully instead of dropping active sessions. We will also cover the pitfalls that catch teams off guard the first time they run a dedicated server fleet on interruptible capacity, plus a full troubleshooting section for when FleetIQ refuses to place sessions where you expect.
This is a backend infrastructure tutorial, not a game-design one, so the examples assume you already have a working dedicated server binary or container and just need the cloud plumbing around it. If you’re starting from a managed hosting service rather than raw EC2 and GameLift, the cost-mixing principles here still apply conceptually, but the exact commands will differ. The architecture decisions below (instance diversification, a non-interruptible floor fleet, and a termination handler that treats shutdown as a normal event) hold regardless of which cloud provider or orchestration layer ends up running the actual containers.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why game server cloud costs spiral out of control
Dedicated game servers are stateful in a way that most web backends are not. A checkout API can scale to zero between requests; a game session cannot, because disconnecting mid-match ends the game for every player in the room. That difference pushes most teams toward over-provisioning: keep enough On-Demand instances running that a sudden surge in concurrent players never gets stuck in a matchmaking queue. The result, according to AWS’s own GameLift pricing documentation, is that studios using the traditional approach of buying a fixed amount of servers that run continuously all month pay for capacity whether or not anyone is using it.
GameLift’s pricing page illustrates the gap with a concrete example: autoscaling that turns off idle game servers can meaningfully cut costs compared with running the same fleet continuously at peak size. Combine autoscaling with Spot Instances and the two savings mechanisms stack, so you pay less per instance-hour and run fewer instance-hours overall. The catch is that Spot capacity is reclaimable. AWS can take an instance back with two minutes of warning, and a naively built game server will drop every player connected to that instance when it happens. This tutorial builds around that constraint rather than ignoring it.
Prerequisites
Before starting, make sure you have the following in place. Version numbers matter here because GameLift’s SDK and FleetIQ APIs have changed shape across releases, and a mismatched SDK version is one of the most common setup failures.
- An AWS account with billing alerts enabled and IAM permissions for EC2, Auto Scaling, GameLift, and CloudWatch
- AWS CLI version 2.17 or later, authenticated with
aws configure - Amazon GameLift Server SDK version 5.x for your game server’s language (C++, C#, and Go bindings are current)
- A containerized or binary-packaged dedicated game server build that reads its listening port from a runtime parameter rather than a hardcoded value
- Docker 25.x or later if you are packaging the server as a container for GameLift Servers container fleets
- Terraform 1.9+ or the AWS CDK 2.x if you want to manage the Auto Scaling group and launch templates as code (recommended, used in the examples below)
- A basic understanding of EC2 instance families, since Spot availability varies by instance type and Availability Zone
- Budget set aside for at least one On-Demand “floor” fleet, since an all-Spot setup is not recommended for anything other than throwaway test environments
Step 1: Decide which capacity model fits your game
Before touching the console, decide how much of your fleet can tolerate interruption. A turn-based mobile game can run almost entirely on Spot, since a reconnect after a two-minute warning is barely noticeable. A competitive first-person shooter with twenty-minute matches needs a larger On-Demand floor, because losing a server mid-round is a worse player experience than a brief queue delay. AWS’s GameLift FleetIQ best-practices documentation recommends mixing fleet types and spreading sessions across multiple EC2 instance families precisely because a single Spot pool can dry up without warning.
Write this decision down before you provision anything: what percentage of peak capacity is Spot, what percentage is On-Demand, and what session length makes a Spot interruption acceptable versus disruptive. You will encode these numbers directly into the Auto Scaling group weights in Step 6.
Step 2: Package the game server build for GameLift
GameLift Servers needs your dedicated server binary wrapped with the GameLift Server SDK so it can report session state back to the service. The SDK integration is a handful of callbacks: one to report the server is ready for players, one to accept or reject a new game session, and, critically for Spot, one to handle a termination notice.
// Minimal GameLift Server SDK integration (C++)
#include <aws/gamelift/server/GameLiftServerAPI.h>
Aws::GameLift::Server::InitSDKOutcome initOutcome =
Aws::GameLift::Server::InitSDK();
if (!initOutcome.IsSuccess()) {
LogFatal("GameLift SDK init failed: %s",
initOutcome.GetError().GetErrorMessage());
return 1;
}
auto onStartGameSession = [](Aws::GameLift::Server::Model::GameSession session) {
// Validate session parameters, then:
Aws::GameLift::Server::ActivateGameSession();
};
auto onProcessTerminate = []() {
// Fired on Spot interruption OR normal scale-down.
// You get a short window to drain players before the process dies.
NotifyConnectedPlayersOfShutdown();
Aws::GameLift::Server::ProcessEnding();
};
auto onHealthCheck = []() { return true; };
Aws::GameLift::Server::ProcessParameters params(
onStartGameSession, nullptr, onProcessTerminate, onHealthCheck,
7777, Aws::GameLift::Server::LogParameters({"/local/game/logs"}));
Aws::GameLift::Server::ProcessReady(params);
The onProcessTerminate callback is the single most important piece of code in this tutorial. It fires both for a normal scale-down and for a Spot reclamation, and it is your only hook to tell players the server is going away before the instance disappears.
Step 3: Build and upload the GameLift build artifact
Once the SDK is wired in, zip the build and upload it to GameLift, or push a container image if you are using GameLift Servers container fleets, now the recommended path for teams already running Docker pipelines.
# Upload a traditional build artifact
aws gamelift upload-build \
--name "my-game-server" \
--build-version "1.4.0" \
--build-root ./build-output \
--operating-system AMAZON_LINUX_2023 \
--region us-east-1
# OR, for a container-based build:
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
docker build -t my-game-server:1.4.0 .
docker tag my-game-server:1.4.0 \
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-game-server:1.4.0
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-game-server:1.4.0
Step 4: Create an EC2 launch template with instance diversity
FleetIQ and EC2 Fleet both work better with a wide set of eligible instance types. AWS’s purchasing-options documentation explains why: the capacity-optimized allocation strategy picks instances from the Spot pools with the deepest available capacity, which lowers interruption risk, but it can only do that if you give it options. A launch template pointed at a single instance type defeats the purpose.
resource "aws_launch_template" "game_server" {
name_prefix = "game-server-"
image_id = data.aws_ami.al2023.id
instance_type = "c6g.xlarge" # default, overridden per-fleet by mixed_instances_policy
network_interfaces {
associate_public_ip_address = true
security_groups = [aws_security_group.game_server.id]
}
user_data = base64encode(templatefile("bootstrap.sh.tpl", {
gamelift_fleet_id = aws_gamelift_fleet.main.id
}))
tag_specifications {
resource_type = "instance"
tags = {
Name = "game-server-spot"
}
}
}
Step 5: Create the GameLift fleet with Spot and On-Demand instance roles
GameLift fleets can mix fleet types directly, letting FleetIQ place new sessions on Spot capacity first and fall back to an On-Demand fleet when Spot pools are thin. This is the architecture AWS documents for reducing game hosting costs with Spot fleets: a Spot-first fleet backed by a smaller On-Demand fleet that absorbs overflow and interruptions.
aws gamelift create-fleet \
--name "spot-primary-fleet" \
--build-id "build-1234567890abcdef" \
--ec2-instance-type c6g.xlarge \
--fleet-type SPOT \
--ec2-inbound-permissions FromPort=7777,ToPort=7777,IpRange=0.0.0.0/0,Protocol=UDP \
--runtime-configuration '{
"ServerProcesses": [{"LaunchPath": "/local/game/server", "Parameters": "-port 7777", "ConcurrentExecutions": 1}]
}' \
--region us-east-1
aws gamelift create-fleet \
--name "ondemand-backup-fleet" \
--build-id "build-1234567890abcdef" \
--ec2-instance-type c6g.xlarge \
--fleet-type ON_DEMAND \
--ec2-inbound-permissions FromPort=7777,ToPort=7777,IpRange=0.0.0.0/0,Protocol=UDP \
--runtime-configuration '{
"ServerProcesses": [{"LaunchPath": "/local/game/server", "Parameters": "-port 7777", "ConcurrentExecutions": 1}]
}' \
--region us-east-1
Step 6: Build a queue that prioritizes Spot without starving players
A GameLift queue sits in front of both fleets and decides where to place each new session, factoring in player latency, instance price, and GameLift’s own prediction of Spot interruption likelihood for that pool. Order the destinations so Spot is tried first, with the On-Demand fleet as the documented fallback.
aws gamelift create-game-session-queue \
--name "main-queue" \
--destinations '[
{"DestinationArn": "arn:aws:gamelift:us-east-1::fleet/fleet-spot-primary"},
{"DestinationArn": "arn:aws:gamelift:us-east-1::fleet/fleet-ondemand-backup"}
]' \
--player-latency-policies '[{
"MaximumIndividualPlayerLatencyMilliseconds": 100,
"PolicyDurationSeconds": 10
}]' \
--timeout-in-seconds 60
Step 7: Configure FleetIQ-aware Auto Scaling
If you are running the EC2-direct FleetIQ path instead of native GameLift fleets, the scaling unit is an EC2 Auto Scaling group behind a game server group, not a single instance type. Set a minimum size that covers your off-peak floor and a maximum that covers your realistic worst-case concurrent-player spike, not an arbitrary round number.
aws gamelift create-game-server-group \
--game-server-group-name "fleetiq-group" \
--min-size 2 \
--max-size 40 \
--launch-template '{"LaunchTemplateId": "lt-0abc123def456"}' \
--instance-definitions '[
{"InstanceType": "c6g.xlarge", "WeightedCapacity": "1"},
{"InstanceType": "c6gd.xlarge", "WeightedCapacity": "1"},
{"InstanceType": "c7g.xlarge", "WeightedCapacity": "1"},
{"InstanceType": "m6g.xlarge", "WeightedCapacity": "1"}
]' \
--balancing-strategy SPOT_PREFERRED \
--auto-scaling-policy '{
"TargetTrackingConfiguration": {"TargetValue": 75}
}' \
--role-arn arn:aws:iam::123456789012:role/GameLiftFleetIQRole
Four instance families are listed here on purpose. AWS’s FleetIQ best-practices guidance specifically recommends diversifying across sizes within a family and across families, since the more eligible pools FleetIQ can draw from, the less likely it is to get stuck without capacity during a regional Spot crunch.
Step 8: Handle the two-minute interruption notice correctly
When AWS needs a Spot instance back, it sends a termination notice roughly two minutes before reclaiming the instance. GameLift surfaces this through the same onProcessTerminate callback used for ordinary scale-down. Treat that window as a drain period, not a migration window. Two minutes is rarely enough to relocate an active match, but it is enough to warn players, stop accepting new joins, and let a match finish early or save state.
def on_process_terminate():
"""
Fires on Spot reclamation OR scale-down. Budget for ~100 seconds
of usable time, not the full two minutes.
"""
broadcast_to_players({
"type": "server_shutdown_warning",
"seconds_remaining": 100,
"reconnect_hint": True,
})
stop_accepting_new_players()
persist_match_state_to_dynamodb()
gamelift_server_api.process_ending()
Step 9: Set CloudWatch alarms tied to cost, not just capacity
Most Auto Scaling tutorials stop at CPU-utilization alarms. For a game server fleet, add a second layer of alarms on the On-Demand backup fleet’s usage so a Spot pool drying up doesn’t silently push your whole fleet onto expensive On-Demand capacity without anyone noticing until the invoice arrives.
aws cloudwatch put-metric-alarm \
--alarm-name "gamelift-ondemand-fallback-spike" \
--metric-name "ActiveInstances" \
--namespace "AWS/GameLift" \
--dimensions Name=FleetId,Value=fleet-ondemand-backup \
--statistic Average \
--period 300 \
--threshold 15 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:cost-alerts
Step 10: Test interruption behavior before launch, not after
AWS provides a way to simulate a Spot interruption on a running instance so you can verify your drain logic actually fires, rather than discovering it fails during a live weekend spike. Run this against a staging fleet before trusting Spot with real players.
aws ec2 send-spot-instance-interruptions \
--instance-ids i-0abc123def456789 \
--region us-east-1
# Watch your game server logs for the onProcessTerminate callback
# firing within a few seconds of this command.
Step 11: Tune the Spot-to-On-Demand ratio from real data
After a week of real traffic, pull interruption and placement metrics and adjust the ratio you set in Step 1. If On-Demand fallback usage is consistently near zero, push more weight onto Spot. If players are reporting mid-match disconnects correlated with Spot interruption events, shift weight back toward On-Demand or add more instance types to the pool.
Step 12: Apply the same model on Azure or Google Cloud for multi-cloud fleets
The pattern in this tutorial is not AWS-exclusive. Microsoft advertises Azure Spot Virtual Machines at discounts of up to 90% versus pay-as-you-go pricing, and Google Cloud advertises Spot VM discounts of up to 91% versus on-demand pricing, with prices that can change as often as daily. The mechanics differ (Azure Spot VM eviction policy varies and can give little to no warning, while Google Cloud Spot VMs give roughly a 30-second preemption notice), but the design principle is identical: diversify instance types, keep a non-interruptible floor, and build your server process to treat termination as a first-class event rather than a crash.
Step 13: Document the rollback plan
Write down the exact command to shift 100% of new sessions to the On-Demand fleet before you need it under pressure. A queue update is the fastest lever you have.
aws gamelift update-game-session-queue \
--name "main-queue" \
--destinations '[{"DestinationArn": "arn:aws:gamelift:us-east-1::fleet/fleet-ondemand-backup"}]'
Working through the actual savings math
Numbers are more convincing than percentages alone, so walk through a realistic fleet before committing engineering time to this migration. Say a mid-size multiplayer title runs a baseline of 20 c6g.xlarge instances around the clock to cover global time zones, with bursts to 60 instances during regional peak hours. At On-Demand pricing, 20 instances running 24/7 for a 30-day month is 14,400 instance-hours just for the floor, before counting a single burst hour. Layer autoscaling on top so the fleet shrinks back to that 20-instance floor outside peak windows instead of staying at 60, and GameLift’s own documentation suggests meaningful savings versus keeping peak-size capacity running continuously. Add Spot pricing for the portion of that fleet that can tolerate interruption, at the 50% to 85% discount AWS advertises for GameLift Servers Spot instances, and the two savings mechanisms compound rather than simply adding together.
The practical way to model this before committing is to export 30 days of CloudWatch concurrency data, bucket it by hour, and calculate three numbers: the instance-hours you’d need under a fixed-peak model, the instance-hours you’d need under autoscaling alone, and the instance-hours you’d need under autoscaling plus a realistic Spot percentage based on your chosen instance types’ interruption-frequency bands. Most teams find the second step (autoscaling) delivers the bulk of the savings, and Spot adds a further discount on top of an already-shrunk instance-hour count rather than being the primary lever. That ordering matters for prioritization: if you only have time to implement one piece of this tutorial first, implement autoscaling in Step 7 before chasing Spot savings in Steps 4 through 6.
Choosing between GameLift fleets, FleetIQ, and container fleets
AWS offers three overlapping ways to run GameLift workloads, and picking the wrong one for your team’s existing tooling adds friction you don’t need. Native GameLift fleets, the approach used in Steps 5 and 6 of this tutorial, hand instance lifecycle entirely to GameLift and are the fastest path if your build is a traditional binary with no existing container pipeline. GameLift FleetIQ, used in Step 7, is the right call if you already manage EC2 Auto Scaling groups directly and want GameLift’s interruption-aware placement logic layered on top of infrastructure you control. GameLift Servers container fleets sit closest to how most backend teams already ship code: if your game server is already packaged as a Docker image for a Kubernetes or Nomad deployment elsewhere in your stack, container fleets let you reuse that image without rewriting your build pipeline around GameLift’s traditional zip-upload flow.
Teams coming from a Kubernetes-based setup, including the Agones pattern used in GameLift’s own open-source reference architecture, often ask whether to migrate off Kubernetes entirely to adopt GameLift’s Spot handling. That’s rarely necessary. Agones already exposes its own interruption-handling hooks through the Kubernetes node drain mechanism, and the same capacity-optimized Spot allocation strategy covered in Step 4 applies whether the underlying compute is a bare EC2 Auto Scaling group or a Kubernetes node group backing an Agones fleet. The choice between GameLift-native and Kubernetes-native Spot handling comes down to which orchestration layer your team already operates, not which one saves more money, since the underlying EC2 Spot discount is identical either way.
Monitoring beyond fleet utilization
The CloudWatch alarm in Step 9 catches one failure mode: a Spot shortfall quietly shifting load onto expensive On-Demand capacity. A production dashboard needs a few more signals before you can trust the fleet unattended overnight. Track the interruption rate per instance type over a rolling seven-day window, since AWS’s Spot Instance Advisor bands can shift between regions and weeks as overall EC2 demand changes. Track average session length against interruption timing, so you can tell whether most interruptions are landing during the low-stakes early minutes of a match or disrupting established late-game sessions. Track queue placement latency separately for the Spot-primary and On-Demand destinations in your game session queue, since a growing gap between the two is an early sign that Spot capacity in a given region is tightening before your cost dashboard shows the effect.
aws cloudwatch get-metric-statistics \
--namespace "AWS/GameLift" \
--metric-name "PercentAvailableGameSessions" \
--dimensions Name=FleetId,Value=fleet-spot-primary \
--start-time 2026-09-24T00:00:00Z \
--end-time 2026-10-01T00:00:00Z \
--period 3600 \
--statistics Average Minimum
A sustained drop in PercentAvailableGameSessions on the Spot-primary fleet, without a corresponding rise in player count, is usually the first measurable sign of a regional Spot shortage, well before players start reporting problems or the On-Demand fallback alarm from Step 9 fires.
Security considerations specific to Spot-backed game servers
Spot capacity churns more than On-Demand capacity by design, which means your fleet’s security posture needs to assume a shorter-lived, higher-turnover instance population. Bake security group rules and IAM instance profiles into the launch template from Step 4 rather than applying them manually after launch, since a manually patched Spot instance that gets reclaimed and replaced will silently revert to the unpatched template state. Rotate any credentials baked into the AMI or user data on a schedule that assumes instances churn weekly rather than monthly, since Spot reclamation means your fleet’s actual instance population turns over far faster than a traditional always-on server farm. Finally, make sure your game server’s logging pipeline ships logs off-instance before ProcessEnding() is called in the termination handler from Step 8, since local logs on a reclaimed Spot instance are gone the moment AWS takes the instance back, and that is exactly the moment you most want a record of what happened.
Spot interruption-frequency bands and what to do about each
| Interruption-frequency band | What it means | Recommended action for game fleets |
|---|---|---|
| Under 5% | Historical average across most AWS Regions and instance types | Safe as a primary pool for session lengths under 30 minutes |
| 5%â10% | Moderate churn, still broadly usable | Keep as one of several diversified instance types, not the sole pool |
| 10%â15% | Noticeably higher reclamation risk | Weight lower in the game server group; monitor closely |
| 15%â20% | High churn relative to typical pools | Avoid for long-session modes; acceptable for short casual matches |
| Over 20% | Pool is under heavy demand pressure | Exclude from the launch template until the band improves |
AWS’s Spot Instance Advisor publishes these bands per instance type and Region and updates them based on trailing-month data, so treat the instance list in Step 7’s game server group as something you revisit monthly rather than a one-time decision baked in at launch.
Cost comparison: Spot-mixed fleet vs. all On-Demand
| Capacity model | Discount vs. On-Demand | Interruption notice | Best fit |
|---|---|---|---|
| AWS EC2 Spot (plain) | Up to 90% | 2 minutes | Stateless or short-session workers |
| Amazon GameLift Servers Spot | 50%â85% | 2 minutes | Dedicated game server fleets with FleetIQ |
| GameLift autoscaling (idle shutdown) | ~85% vs. always-on peak sizing | Not applicable | Variable-demand multiplayer titles |
| Azure Spot Virtual Machines | Up to 90% | Policy-dependent, can be near-zero | Azure-native game backends |
| Google Cloud Spot VMs | Up to 91% | About 30 seconds | GCP-native batch or session workers |
| All On-Demand (no Spot) | 0% (baseline) | None | Ranked or competitive matches with zero interruption tolerance |
FleetIQ allocation strategies compared
| Allocation strategy | How it picks instances | Interruption risk | When to use it |
|---|---|---|---|
| Capacity-optimized | Launches from the Spot pools with the deepest available capacity | Lower | Default choice for session-hosting fleets |
| Lowest-price | Picks whichever eligible pool is cheapest right now | Higher | Batch jobs with no session continuity to protect |
| Price-capacity-optimized | Balances price against pool depth | Medium | Mixed workloads with moderate interruption tolerance |
| Diversified | Spreads evenly across all specified pools | Medium | Early testing before you have interruption-rate data |
Five common pitfalls when moving game servers to Spot
- Running one instance type. A single Spot pool can dry up instantly in one Availability Zone. Every example in this tutorial lists at least three to four eligible instance types for exactly this reason.
- Ignoring the termination callback entirely. Teams that skip
onProcessTerminateimplementation find out the hard way when a Spot reclamation silently kills active matches with no warning to players. - Setting the On-Demand backup fleet to zero. A zero-capacity backup fleet means there’s nowhere for FleetIQ to fall back to when every eligible Spot pool is briefly unavailable, and new sessions simply fail to place.
- Scaling only on CPU utilization. Game server CPU usage doesn’t track player count linearly; scale on active game sessions or available server slots instead, as recommended in GameLift’s own autoscaling guidance.
- Assuming the full two minutes is usable. Network delivery and processing overhead eat into the interruption notice; budget for roughly 90 to 100 seconds of real drain time, not the full 120.
Example output: a healthy mixed fleet
$ aws gamelift describe-fleet-utilization --fleet-ids fleet-spot-primary fleet-ondemand-backup
{
"FleetUtilization": [
{
"FleetId": "fleet-spot-primary",
"ActiveServerProcessCount": 34,
"ActiveGameSessionCount": 31,
"CurrentPlayerSessionCount": 118,
"MaximumPlayerSessionCount": 160
},
{
"FleetId": "fleet-ondemand-backup",
"ActiveServerProcessCount": 4,
"ActiveGameSessionCount": 2,
"CurrentPlayerSessionCount": 7,
"MaximumPlayerSessionCount": 32
}
]
}
A ratio like this, with most load on the Spot-primary fleet and the On-Demand backup handling only overflow, is the signal that the queue and weighting are configured correctly. If the On-Demand fleet is regularly carrying more than 20% to 30% of active sessions, that’s a sign Spot capacity in your region or instance family is thinner than expected, and it’s worth revisiting Step 11.
Migration checklist for teams moving off an always-on fleet
Moving a live game from an always-on, fixed-size fleet to the Spot-and-autoscaling model in this tutorial is safer done in stages than as a single cutover. Start by deploying the new GameLift fleets from Steps 5 and 6 alongside your existing infrastructure, with the game session queue still pointed entirely at the old setup, so you can validate the new build and SDK integration without any player traffic depending on it. Next, shift a small percentage of new sessions, perhaps 5% to 10%, onto the new Spot-primary fleet by adjusting queue destination weighting, and watch the monitoring signals from the dashboard section above for at least a full week to capture both weekday and weekend traffic patterns. Only after that soak period shows stable placement latency and an acceptable interruption rate should you increase the Spot-primary fleet’s share toward its target steady state.
Keep the old always-on fleet running at reduced capacity as a manual fallback during this entire migration window rather than decommissioning it immediately. The rollback script from Step 13 assumes the On-Demand backup fleet from this same migration already exists and is warm; it is not designed to stand up an entirely new fleet from zero under incident pressure. Once the new architecture has run a full billing cycle and the cost reduction shows up on the actual AWS invoice rather than just in projected estimates, that’s the right time to decommission the legacy fleet and reclaim its reserved capacity or Savings Plan commitments for other workloads.
Troubleshooting
- New sessions won’t place on the Spot fleet at all. Check that the launch template’s security group allows inbound traffic on the game port and that the IAM role attached to the game server group has the GameLift FleetIQ managed policy.
- Instances launch but never register as healthy. The GameLift Server SDK likely isn’t calling
ProcessReady()before the health-check timeout; check the game server’s local logs for SDK initialization errors. - Players report disconnects with no warning. The
onProcessTerminatecallback either isn’t wired up or isn’t broadcasting to connected clients before callingProcessEnding(). - Auto Scaling group keeps hitting max size. Your max size was set from a guess rather than real peak-concurrency data; pull your highest observed concurrent-player count from the last 30 days and size to that plus headroom.
- Costs didn’t drop as much as expected. Check the On-Demand backup fleet’s utilization. If it’s regularly absorbing a large share of sessions, your region’s Spot pools for the chosen instance types may be undersupplied; add more instance families.
- Spot Instance Advisor shows a high interruption-frequency band for your instance type. Swap to an instance type in a lower interruption-frequency band, or add it as one option among several rather than the sole choice.
- FleetIQ game server group is stuck in a suspended state. This typically follows repeated launch failures; check the Auto Scaling activity history for the specific EC2 launch error before retrying.
- Billing alarm fires even though the Spot fleet looks healthy. Confirm the alarm is scoped to the On-Demand fleet’s
ActiveInstancesmetric and not accidentally tracking the Spot fleet, which will naturally show more churn.
Advanced tips for production fleets
Once the base setup is stable, a few refinements make a measurable difference. First, separate your fleets by region and let GameLift’s player-latency policy route sessions to the nearest healthy fleet rather than running one global queue. This also means a regional Spot shortage in one area doesn’t affect players elsewhere. Second, track interruption rate per instance type over time and retire instance types that consistently land in the higher interruption-frequency bands reported by the Spot Instance Advisor. Third, if your game supports it, build mid-match server migration for long-session modes instead of relying purely on graceful shutdown. That’s more engineering work, but it removes the player-facing cost of interruptions almost entirely. Finally, treat the On-Demand backup fleet size as a tunable cost knob: too small and players experience placement failures during Spot shortages, too large and you’re paying for standby capacity that defeats the purpose of this whole exercise.
Complete working project structure
Putting every step together, a production-ready repository for this setup looks like this:
game-server-spot-fleet/
âââ server/
â âââ main.cpp # GameLift SDK integration, onProcessTerminate handler
â âââ Dockerfile
âââ infra/
â âââ launch_template.tf # EC2 launch template, instance diversity
â âââ game_server_group.tf # FleetIQ game server group + Auto Scaling policy
â âââ gamelift_fleets.tf # Spot-primary + On-Demand backup fleets
â âââ gamelift_queue.tf # Queue with latency policy and fleet priority
â âââ cloudwatch_alarms.tf # Cost and fallback-usage alarms
âââ scripts/
â âââ upload_build.sh
â âââ simulate_interruption.sh
â âââ rollback_to_ondemand.sh
âââ README.md
This structure keeps the drain logic in the server code where it belongs, the capacity mix in Terraform where it’s auditable, and the emergency rollback as a one-command script rather than something you have to remember how to do at 2 a.m. during a live incident.
Frequently asked questions
Is it safe to run a competitive multiplayer game entirely on Spot Instances?
It’s possible but not advisable for ranked or latency-sensitive competitive modes. A small On-Demand backup fleet handling overflow and absorbing interruptions, as shown in Step 5, is the pattern AWS documents for production use. Casual or asynchronous game modes can tolerate a much higher Spot percentage.
How much warning do I actually get before a Spot Instance is reclaimed?
AWS sends a two-minute interruption notice before reclaiming an EC2 Spot Instance. Budget for slightly less than the full window in your drain logic to account for processing and network delivery delay.
What’s the difference between GameLift Spot fleets and plain EC2 Spot Instances?
GameLift Spot fleets apply GameLift’s own session-placement intelligence on top of EC2 Spot capacity, factoring in player latency, instance price, and predicted interruption likelihood when assigning new sessions. Plain EC2 Spot Instances have none of that game-aware placement logic built in.
Can I use Spot Instances with GameLift FlexMatch?
Yes. A FlexMatch matchmaker can use a queue containing Spot fleets, and GameLift prioritizes placements across Spot and On-Demand fleets for both new sessions and backfill requests.
How many instance types should I include for capacity-optimized allocation to work well?
AWS’s FleetIQ guidance recommends diversifying across both sizes within an instance family and across families. Four or more eligible instance types, as used in this tutorial’s game server group, gives the allocation strategy meaningfully more pools to draw from than one or two would.
Do Azure and Google Cloud offer the same kind of Spot savings for game servers?
Azure Spot Virtual Machines advertise discounts of up to 90% versus pay-as-you-go pricing, and Google Cloud Spot VMs advertise discounts of up to 91% versus on-demand pricing. Neither has a GameLift-equivalent managed game-hosting layer, so you’d implement the FleetIQ-style placement logic yourself on top of their respective autoscaling tools.
What happens to billing if my Spot Instance gets interrupted mid-hour?
AWS documentation notes that billing after an interruption depends on the operating system and the specific circumstances of the interruption. In some cases you’re billed for only the seconds used, in others for the full hour. Don’t assume either outcome; check current EC2 Spot billing documentation for your instance type.
Should I set up Spot Instances before or after my game launches?
Build and test the mixed-fleet architecture before launch, using the interruption-simulation command from Step 10 against a staging fleet. Retrofitting graceful shutdown handling onto a live game with real players connected is far riskier than validating it ahead of time.
Which instance types should I avoid for Spot-backed game servers?
Avoid relying on a single large, specialized instance type (GPU-backed types or the newest generation with limited capacity) as your only Spot pool, since these tend to show higher and more volatile interruption-frequency bands on the Spot Instance Advisor. General-purpose and compute-optimized families with multiple sizes available, like the c6g and m6g families used in this tutorial’s examples, typically offer deeper and steadier Spot capacity.
Do I need to rewrite my game server’s networking code to use Spot Instances?
No. Spot Instances are ordinary EC2 instances under the hood, so your existing networking, port binding, and client-connection code works unchanged. The only required code change is implementing the onProcessTerminate callback from Step 2 so your server can react to a termination notice instead of simply disappearing.





