A configuration change looks harmless. You flip a feature flag, adjust a timeout, raise a rate limit, and push it out. There's no build, no deploy pipeline, no code review gate. That's the appeal of dynamic configuration: you change behavior in seconds without shipping code.
It's also the risk. When a configuration change deploys to 100% of your fleet at once, a bad value reaches every user before you can react. The blast radius is the whole system. The time to notice is however long it takes an alarm to fire, a customer to complain, or a dashboard to turn red. By then it's everywhere.
AWS AppConfig is built around a different assumption: change configuration slowly and watch the metrics while it rolls out. You can't start an AppConfig deployment without choosing a deployment strategy. The strategy says how fast the change spreads and how long AppConfig keeps watching afterward. This post explains how deployment strategies work, what AppConfig needs from you to make the watching real, and why "slowly, while watching" is the single most important habit for safe configuration changes.
The problem with all-at-once
Imagine you lower a downstream call timeout, managed in AppConfig, from 3,000 ms to 1,500 ms so slow requests stop tying up threads. The value is perfectly valid. A schema validator would wave it through. What you don't know is that the downstream's tail latency sits around 1,800 ms at peak. Deploy it all at once and every caller picks up the new value within a minute or so. Requests that used to succeed slowly now time out. Callers retry, the retries pile on load, and the failure is fleet-wide before your on-call has finished reading the first alert.
Now imagine you roll the same change out to 10% of your fleet first, with Amazon CloudWatch alarms on the environment so AppConfig can watch your error rate. The bad value still ships, but it reaches a tenth of your callers. An alarm goes off, and AppConfig rolls the deployment back. The other 90% never receive it, and the 10% that did receive it get the old value back on their next poll. Same mistake, a fraction of the damage.
That's the whole idea. You can't stop every bad value from being created. A value that's valid but still wrong, like this one, slips past validators and only reveals itself in production. What a gradual rollout gives you is time and containment: the change reaches a small slice of your fleet long enough for your metrics to say whether it's healthy, before it reaches everyone.
A quick orientation
Before we get into the mechanics, a few terms are worth pinning down. In AppConfig, an application is a logical grouping for one app's configuration. Within it, an environment is a place you deploy to, such as production or staging. A configuration profile is one piece of configuration data you deploy: a set of feature flags, or a free-form JSON, YAML, or text document. A deployment takes a version of one profile and rolls it out to one environment.
That last step, rolling a profile's new version out to an environment, is where a deployment strategy comes in. It governs the rollout itself, and it's what the rest of this post is about.
Rollout progress is measured in targets, and that word earns a precise definition because it does a lot of work below. When AppConfig says a deployment has reached 10% of targets, it's counting the callers that poll for the configuration, not machines. Each caller holds a configuration session, and AppConfig decides session by session whether it's in the current slice. One AppConfig Agent per host makes a target effectively a host. Several sessions per host, or Lambda execution environments, make the mapping looser. You can also use the AppConfig Agent to roll out by user or segment instead, so a given user sees one consistent version throughout the rollout no matter which host serves them. See entity-based gradual deployments.
What a deployment strategy is
Every deployment names a deployment strategy: a reusable definition of how fast the change rolls out and how long AppConfig watches before calling it done. A strategy isn't tied to an application, so you define it once and select it on each deployment. "How we roll out config" becomes something you decide up front and pick from a list, not something you improvise every time.
A deployment strategy is built from four settings:
- Growth type: the shape of the rollout curve, linear or exponential.
- Growth factor: how big each step is, as a percentage of targets.
- Deployment time: the total window over which those steps are spread.
- Bake time: how long AppConfig keeps watching your CloudWatch alarms after the configuration reaches 100% of targets, before it considers the deployment complete.
Get these four right and you've described exactly how cautiously, or how quickly, a change reaches your fleet. Let's take them one at a time.
Growth type: the shape of the curve
Growth type controls how the percentage of targets grows at each step. AppConfig supports two, linear and exponential. The console calls this setting deployment type.
Linear grows by the same increment every step. A rollout with a 20% growth factor moves 0% → 20% → 40% → 60% → 80% → 100%, in even steps across the deployment window. Every interval exposes the same additional slice of your fleet, which makes it predictable and easy to reason about.
Exponential starts small and accelerates. It follows the formula G * (2^N), where G is the growth factor and N is the step number, counting from zero. With a growth factor of 2, the rollout reaches 2% of targets, then 4%, then 8%, then 16%, and so on until it hits 100%. This keeps early exposure very small at the riskiest moment (when you know least about the change) and widens quickly once the first steps clear. It's the classic canary shape: tiptoe out, then stride once nothing has broken. The trade-off is at the far end, where the steps are big jumps. With a 10% growth factor they run 40% → 80% → 100%, so a problem that only surfaces at majority exposure arrives in one leap rather than several.
As a rule of thumb: exponential for maximum caution at the start, linear for steady, evenly paced exposure all the way through.
Growth factor: the size of each step
The growth factor is the percentage of targets that receive the configuration during each step. It can be anything from 1 to 100. The console calls it step percentage; the API, CLI, and SDKs call it growth factor.
For linear growth, the factor is the size of every step. For exponential growth, it's the G in G * (2^N): the size of the first step, which then doubles each step until the configuration reaches all targets. The final step is capped at 100%.
Deployment time: how long the whole thing takes
Deployment time is the total window over which AppConfig spreads the steps. Note what it is not: a timeout. It's the window during which the deployment is processed in intervals. A 30-minute deployment with 20% linear growth advances one 20% step every six minutes until it reaches 100%. Exponential works the same way. A 10% growth factor takes five steps to reach 100% (10 → 20 → 40 → 80 → 100%), so a 20-minute exponential deployment advances roughly every four minutes.
One detail is easy to miss: AppConfig doesn't push configuration to callers. They poll for it. "Reached 20% of targets" means 20% of sessions are now eligible, and each picks up the new version on its next poll. So real exposure lags the deployment's percentage by up to one poll interval. The default is 45 seconds with the AppConfig Agent. The same lag applies in reverse. After a rollback, callers that got the bad value revert on their next poll.
Deployment time is your main lever for pacing. A longer window gives your metrics more time to reveal a problem at each stage. A shorter one ships faster but leaves less room to notice trouble between steps.
Bake time: watching after full deployment
Reaching 100% of targets isn't the end of the deployment. Bake time is how long AppConfig keeps monitoring your CloudWatch alarms after the configuration reaches every target, before marking the deployment complete.
It matters because some problems don't surface during the rollout itself: a slow memory leak, a cache that only fills after a few minutes, a scheduled job that fires on the hour. Bake time keeps AppConfig watching for a defined window afterward. If an alarm goes off during it, AppConfig rolls the deployment back exactly as it would mid-rollout. The next section covers what AppConfig needs from you for that to happen.
Once bake time ends, the deployment is complete and automatic rollback is off. If you discover a problem after that, reverting is a deliberate act rather than an automatic one. See Reverting a configuration.
Alarms: the setup that makes rollback automatic
Everything above about AppConfig "watching your alarms" depends on a setup step that's easy to skip. On its own, AppConfig knows nothing about your application's health. You attach one or more CloudWatch alarms to the environment, plus an IAM role AppConfig can assume to read them. With that in place, AppConfig checks those alarms throughout the rollout and the bake time. It rolls back automatically if any goes off, whether at 10% exposure or in the last minute of the bake.
Without it, a deployment strategy still slows the rollout down, but nothing stops it for you. You watch the dashboard yourself, and your safety net is stopping an in-progress deployment by hand, which rolls it back. That's a reasonable way to run a small system, as long as you know that's the mode you're in.
Three details are worth knowing before you rely on automatic rollback:
-
It triggers on
INSUFFICIENT_DATA, not only onALARM. That's the conservative choice, but it has a catch. An alarm whose metric goes quiet during a rollout, such as one on a low-traffic endpoint or one that only reports when an error occurs, can roll back a perfectly healthy deployment. The fix is to match the alarm's missing-data treatment to the metric: a metric that reports continuously must treat missing data as breaching, while one that only emits on errors must treat it as not breaching. (The default treatment ismissing, which is what sends a quiet alarm toINSUFFICIENT_DATA.) Also give a new alarm time to evaluate before you deploy, since a freshly created alarm sits inINSUFFICIENT_DATAuntil its first evaluation. - It ignores alarms whose actions are disabled. If someone silenced an alarm for maintenance, it won't roll back your deployment either.
- Your metrics don't have to live in CloudWatch. If you monitor with a third-party tool, an AppConfig extension can check it at each deployment tick and trigger the same rollback. AWS publishes examples for Datadog and New Relic.
The wiring is covered in Monitoring deployments for automatic rollback.
Predefined strategies vs. rolling your own
You don't have to design a strategy from scratch. AppConfig ships four predefined strategies that cover the common cases:
| Strategy | Rollout | Bake time | Best for |
|---|---|---|---|
AppConfig.Canary10Percent20Minutes |
Exponential, 10% growth factor, 20 minutes (10 → 20 → 40 → 80 → 100%) | 10 minutes | Production; caution up front, AWS-recommended |
AppConfig.Linear20PercentEvery6Minutes |
Linear, 20% every 6 minutes, 30 minutes (20 → 40 → 60 → 80 → 100%) | 30 minutes | Production; steady pace and a long bake, AWS-recommended |
AppConfig.AllAtOnce |
100% immediately | 10 minutes | Low-risk changes and emergencies, when you accept full exposure |
AppConfig.Linear50PercentEvery30Seconds |
Linear, 50% every 30 seconds, 1 minute (50 → 100%) | 1 minute | Testing and demos only, not production |
Two things stand out in that table. First, even AllAtOnce isn't unmonitored. The change ships to everyone immediately, but AppConfig still bakes for 10 minutes. So with alarms on the environment you get automatic rollback even when you skip the gradual part. What you lose is containment: by the time an alarm fires, every caller already has the value. Second, Linear50PercentEvery30Seconds is for testing and demos. Its one-minute duration and bake time are deliberately too short to catch much.
For most production workloads, one of the two AWS-recommended strategies is the right starting point: Canary10Percent20Minutes or Linear20PercentEvery6Minutes.
The predefined strategies won't always fit. Maybe you want a very long bake time for a change you're especially nervous about, or a smaller first step for a large fleet. Then you create your own. Here's a custom strategy that rolls out exponentially at a 10% growth factor over an hour, then bakes for a full hour:
aws appconfig create-deployment-strategy \
--name "Cautious-Exponential-1Hour" \
--deployment-duration-in-minutes 60 \
--growth-type EXPONENTIAL \
--growth-factor 10 \
--final-bake-time-in-minutes 60 \
--replicate-to NONE
The same strategy in AWS CDK:
import * as appconfig from 'aws-cdk-lib/aws-appconfig';
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class DeploymentStrategyStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new appconfig.DeploymentStrategy(this, 'CautiousExponential', {
deploymentStrategyName: 'Cautious-Exponential-1Hour',
rolloutStrategy: appconfig.RolloutStrategy.exponential({
growthFactor: 10,
deploymentDuration: Duration.minutes(60),
finalBakeTime: Duration.minutes(60),
}),
});
}
}
The four settings map directly onto the parameters. In the CLI they're --growth-type, --growth-factor, --deployment-duration-in-minutes, and --final-bake-time-in-minutes. In CDK they're RolloutStrategy.exponential with growthFactor, deploymentDuration, and finalBakeTime. The --replicate-to flag isn't a rollout setting. It's optional, and it controls whether AppConfig also saves the strategy as an AWS Systems Manager document. NONE keeps it in AppConfig only. Once created, a custom strategy is selectable anywhere a predefined one is: the console, CLI, API, or infrastructure code.
That snippet is a complete AWS CDK stack. Drop it into a CDK app, run cdk synth, and deploy it as-is to create the strategy. The posts that follow in this series build on the same pattern, each one standing up its piece with a self-contained stack you can paste in and deploy.
Choosing a strategy
There's no universally correct strategy. The right one depends on how much risk a given change carries, how many targets you're rolling it out to, and how quickly your metrics react. A few guidelines:
- Higher-risk changes deserve a slower ramp and a longer bake. Say a bad value would be expensive: a pricing flag, a change to an auth path, anything touching data. Lean toward exponential growth and a generous bake time. Small early exposure plus a long watch is exactly what you want when the downside is large.
- Larger fleets let you start smaller. With thousands of targets, a 1% to 2% first step still covers enough sessions to produce a real signal, and exponential growth lets you start that small without dozens of steps to reach 100%. With a fleet of twenty, a 2% first step probably reaches no one, so a bigger step, or linear growth, makes more sense.
- Make each step longer than your metrics' reaction time. Some signals, such as latency percentiles or error rates averaged over a window, take minutes to appear. When they do, the next step starts before the last one has been judged, unless the step interval outlasts that lag. The interval is deployment time divided by the number of steps. A rollout faster than your metrics is flying blind.
- Size the bake time for problems that only appear at full exposure. Queue backlogs, cache churn, and hourly jobs may not register until everyone is on the new value. The bake window is the only monitored time you get after 100%.
-
Reserve fast strategies for low-stakes changes, emergencies, or non-production.
AllAtOnceis fine for a copy tweak behind a flag, and it's the right call for a kill switch you need everywhere now. It's the wrong one for anything that could take the system down.
Two constraints shape all of this. First, you choose the strategy per deployment, so nothing forces you to use the same one for a routine flag flip and an emergency change. Second, an environment accepts one deployment at a time. While a two-hour canary is in flight, the next change waits until the canary completes, unless you stop the canary to make room, which rolls it back. That's the real cost of a long strategy, so decide in advance what you'll do if an urgent change lands mid-rollout.
The habit to build is to ask, for every configuration change: if this value is wrong, how much do I want exposed before I find out? The answer picks your strategy.
Why this matters
Configuration changes feel low-stakes because they're fast and code-free, and that's exactly why they're dangerous. A deployment strategy puts a governor on that speed. Instead of arriving everywhere at once, a change arrives a slice at a time, under observation, with a monitored window at the end. The cost is time: minutes for the recommended strategies, hours for a cautious custom one, during which the environment is committed to that deployment. In return, a bad value is contained to a fraction of your fleet instead of all of it, your metrics get time to catch it, and, with alarms on the environment, AppConfig reverses it without waiting for a human.
The core habit is simple: change configuration slowly, and watch the metrics while you do. The rest of safe configuration management builds on that foundation. You validate values before they deploy, alarm on the right metrics, and plan for emergency changes.
Top comments (0)