Cutting AWS Costs 70% with Spot Instances on Elastic Beanstalk
Hook
Most developers overpay for AWS compute by 70% because Elastic Beanstalk doesn't expose spot instance configuration through its console—but a single environment variable can change that.
Context
AWS Elastic Beanstalk has always been the platform-as-a-service sweet spot for teams who want deployment simplicity without vendor lock-in to proprietary runtimes. You get CloudFormation-powered infrastructure, auto-scaling, and health monitoring while maintaining full control over your application code. But there's a costly catch: by default, Elastic Beanstalk provisions on-demand EC2 instances, which can cost 3-4x more than spot instances for the same compute capacity.
The mandatoryprogrammer/elasticbeanstalk-base repository addresses this pricing disparity by bridging Elastic Beanstalk's simplicity with EC2 spot instances' cost efficiency. While AWS provides native spot instance support in ECS and EKS, Elastic Beanstalk's architecture doesn't expose spot bidding through standard configuration parameters. This creates an awkward choice: migrate to more complex orchestration platforms or accept inflated compute costs. This project offers a third path—a reusable configuration template that modifies Elastic Beanstalk's Auto Scaling Groups to use spot instances, controlled through a simple EC2_SPOT_PRICE environment variable.
Technical Insight
The technical approach centers on Elastic Beanstalk's .ebextensions mechanism, which allows CloudFormation-style resource customization during environment creation. The key innovation is intercepting the Auto Scaling Group configuration before it's finalized and injecting spot instance parameters based on environment variables.
At its core, the configuration modifies the Auto Scaling Launch Configuration to include a SpotPrice parameter. When you set the EC2_SPOT_PRICE environment variable in your Elastic Beanstalk environment (either through the console or EB CLI), the .ebextensions configuration picks it up and applies it to the infrastructure layer. Here's what a typical .ebextensions/spot-instances.config file looks like:
Resources:
AWSEBAutoScalingLaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
SpotPrice:
Fn::If:
- UseSpotInstances
- Ref: SpotPrice
- Ref: AWS::NoValue
Parameters:
SpotPrice:
Type: String
Default: ""
Description: "The maximum spot price to bid"
Conditions:
UseSpotInstances:
Fn::Not:
- Fn::Equals:
- Ref: SpotPrice
- ""
OptionSettings:
aws:elasticbeanstalk:application:environment:
EC2_SPOT_PRICE: "`{"Ref" : "SpotPrice"}`"
This configuration creates a CloudFormation condition that only applies spot pricing when EC2_SPOT_PRICE is set. The Fn::If conditional ensures backwards compatibility—if the environment variable isn't present, the LaunchConfiguration falls back to on-demand instances using AWS::NoValue.
The Docker integration is straightforward since Elastic Beanstalk's Docker platform already handles container orchestration. You provide a standard Dockerfile or Dockerrun.aws.json, and the spot instance configuration operates at the infrastructure layer, transparent to your application. This separation of concerns means you can retrofit existing Elastic Beanstalk Docker applications without modifying application code.
Deploying this looks like:
# Set your spot price (typically 30-70% of on-demand pricing)
eb setenv EC2_SPOT_PRICE=0.05
# Deploy your Docker application
eb deploy
# Verify spot instances are running
aws ec2 describe-instances \
--filters "Name=tag:elasticbeanstalk:environment-name,Values=my-env" \
--query 'Reservations[*].Instances[*].[InstanceId,InstanceLifecycle]'
The real elegance is in how this handles spot instance interruptions. Elastic Beanstalk's health monitoring already watches for instance terminations and automatically replaces them through the Auto Scaling Group. When AWS reclaims a spot instance, the ASG detects the capacity reduction and launches a replacement—either another spot instance if capacity exists at your bid price, or fails to launch until spot capacity returns. This is where the approach shows its limitations for high-availability workloads, but for development environments or batch processing, it's acceptable.
One architectural consideration: Elastic Beanstalk uses a single Launch Configuration for all instances in an environment. You can't mix spot and on-demand instances, which differs from more sophisticated setups in ECS or Kubernetes where you might use spot for 80% of capacity with on-demand fallback. This all-or-nothing approach means you need separate Elastic Beanstalk environments if you want hybrid purchasing strategies.
Gotcha
The fundamental limitation is spot instance volatility. AWS provides only a two-minute warning via CloudWatch Events before terminating spot instances when they need capacity back. Elastic Beanstalk doesn't integrate with these interruption notices in any meaningful way—it simply detects the instance disappeared and tries to launch a replacement. For applications without graceful shutdown handling, this means dropped connections and failed requests.
More problematic is the spot capacity availability issue. If spot instances at your bid price become unavailable, your Auto Scaling Group will fail to launch new instances entirely. During a scaling event or after a deployment, you might find yourself with zero running instances if spot capacity is constrained in your availability zone. The project provides no fallback mechanism to on-demand instances, which sophisticated spot management systems like Karpenter or AWS Batch provide. You're also limited by Elastic Beanstalk's single-instance-type constraint—you can't specify multiple instance types with different spot pricing strategies, which modern spot best practices recommend for availability.
The repository itself shows signs of being a personal utility rather than production-hardened infrastructure. With only 5 stars and no recent updates, you're essentially adopting someone's configuration snippet without community vetting or ongoing maintenance. The lack of comprehensive documentation means you'll need strong CloudFormation and Elastic Beanstalk knowledge to troubleshoot issues. There's no handling for edge cases like spot price spikes, availability zone outages, or integration with AWS Savings Plans.
Verdict
Use if: You're running fault-tolerant workloads like development/staging environments, batch processing jobs, stateless APIs with external load balancing, or CI/CD workers where interruptions cause minimal impact. You're already committed to Elastic Beanstalk's Docker platform and want cost savings without migrating to ECS or EKS. Your team has CloudFormation expertise to customize and maintain .ebextensions configurations, and you can accept potential zero-instance scenarios during spot capacity crunches. Skip if: You need high availability guarantees or have strict SLAs that can't tolerate two-minute interruption windows. Your application requires stateful processing or long-running connections that can't gracefully handle mid-request terminations. You're starting a new project and can choose container orchestration platforms—ECS with spot fleet diversification or EKS with Karpenter offer far superior spot instance management. You need battle-tested, community-maintained infrastructure code rather than adapting a low-visibility personal project. For production workloads, the cost savings rarely justify the operational complexity and availability risks this approach introduces.