Guide · AWS AppConfig · Deployment
AWS AppConfig Deployment Strategies for Safe MCP Server Config Rollouts
An AppConfig deployment strategy controls the blast radius and speed of a configuration change — and for MCP servers running across dozens of Lambda containers or ECS tasks, the strategy you pick determines how many users are affected if a config change is bad. Because AppConfig rolls out configuration to polling clients over time rather than atomically, during any non-trivial rollout some instances are running the old config and some the new. Understanding that window, and pairing the right strategy with each type of config change, is how you ship config safely. See AWS AppConfig for MCP Servers: Dynamic Configuration Without Redeployment for foundational setup, and Feature Flags for MCP Servers with AWS AppConfig for flag-specific rollout patterns.
TL;DR
Use AppConfig.AllAtOnce for kill-switches (disabling a broken tool needs to happen immediately). Use AppConfig.Linear10PercentEvery10Minutes for new feature flags where you want to catch errors before full rollout. Use AppConfig.Exponential10PercentEvery5Minutes for rate limit increases where gradual ramp matters. Wire a CloudWatch alarm on your MCP tool error rate to the AppConfig environment monitor for automatic rollback. AliveMCP's 60-second external probe gives you the first signal that a config change is causing failures — fast enough to call StopDeployment before the bake timer expires.
Why deployment strategy matters for MCP server config
When you deploy a new config version to AppConfig, it does not push the config to all instances simultaneously. Instead, each running Lambda container or ECS task picks up the new config when it next polls AppConfig — typically every 15–45 seconds. The deployment strategy controls what percentage of polling requests AppConfig serves the new config versus the old config at each point in the rollout.
This creates a mixed-config window: during a linear rollout, the first 10% of pollers get the new config immediately, then another 10% every 10 minutes. For the 90 minutes of a full linear rollout, some of your MCP server instances are running the old config and some the new. For most config changes this is fine — rate limit adjustments can be inconsistent for a few minutes. For some changes it matters a great deal: if you are enabling a tool that requires a new IAM permission, instances with the new config will fail until that permission is also present.
| Strategy | Total rollout time | Mixed-config window | Best for |
|---|---|---|---|
| All-at-once | Seconds (next poll cycle) | ~1 poll interval | Kill-switches, emergency disables, config that must be consistent across all instances |
| Linear 10%/10min | 100 minutes | Up to 100 minutes | New feature flags, moderate-risk config changes, when you want time to catch errors before full rollout |
| Exponential 10%/5min | ~50 minutes to 100% | Up to 50 minutes | Rate limit increases, upstream timeout changes, medium-risk changes with known-good baselines |
| Canary 10%/20min | ~20 minutes to 100% after canary | 20-minute canary window | High-risk changes where you want a long observation window at 10% before committing to full rollout |
Built-in deployment strategies
AppConfig ships four built-in strategies that cover the majority of MCP server config use cases. You reference them by ID in a deployment:
AppConfig.AllAtOnce — immediately marks 100% of pollers as eligible for the new config. All instances pick it up on their next poll (within one poll interval). No bake time. Use this for emergency kill-switches: when a tool is returning 500s and you need it disabled within 15–45 seconds, not 100 minutes.
AppConfig.Linear10PercentEvery10Minutes — increases the percentage by 10 every 10 minutes, reaching 100% after 100 minutes. The 10-minute steps give you time to observe CloudWatch metrics after each increment and manually call StopDeployment if you see problems. Use this for new feature flags and any config change that touches previously untested code paths.
AppConfig.Exponential10PercentEvery5Minutes — starts at 10%, then doubles (roughly) every 5 minutes: 10%, 20%, 40%, 80%, 100%. Reaches full rollout in ~25 minutes. The exponential curve means errors from the first cohort surface quickly (within 5 minutes) while the rollout still completes faster than linear. Use this for rate limit changes where you expect the new limits are safe but want a quick validation window.
AppConfig.Canary10Percent20Minutes — deploys to 10% for a 20-minute observation window, then deploys to the remaining 90% all at once. The two-phase nature makes this ideal for high-stakes changes: you get a long bake at 10% and then a fast final commit. Use this for changes to allowedCallers or any security-sensitive config where the canary cohort provides meaningful signal before you commit.
| Config change type | Recommended strategy | Reason |
|---|---|---|
| Disable broken tool (kill-switch) | AllAtOnce | Speed is the priority; blast radius of bad config is already happening |
| Enable new tool (feature launch) | Linear10PercentEvery10Minutes | New code path; slow rollout gives time to catch errors before full exposure |
| Increase rate limit | Exponential10PercentEvery5Minutes | Higher limits stress upstream systems; gradual ramp validates upstream capacity |
| Decrease rate limit (throttling) | AllAtOnce | You want all instances throttled immediately to stop an overload situation |
| Change upstream timeout | Canary10Percent20Minutes | Timeout changes affect long-tail latency; 20-minute canary catches p99 issues |
| Update allowedCallers | Canary10Percent20Minutes | Security-sensitive; canary validates both new entries work and old ones aren't broken |
Creating custom deployment strategies via CDK
When the built-in strategies don't fit — for example, you want a 25%-per-2-minute linear rollout for a 10-minute total deployment — create a custom CfnDeploymentStrategy:
import * as cdk from 'aws-cdk-lib';
import * as appconfig from 'aws-cdk-lib/aws-appconfig';
// Linear: 25% every 2 minutes = 100% in 8 minutes, then 5-minute bake
const fastLinearStrategy = new appconfig.CfnDeploymentStrategy(this, 'FastLinear', {
name: 'Linear25PercentEvery2Minutes',
deploymentDurationInMinutes: 8,
growthFactor: 25,
growthType: 'LINEAR',
finalBakeTimeInMinutes: 5,
// NONE = don't replicate to SSM; SSM_DOCUMENT = replicate to reuse across accounts
replicateTo: 'NONE',
description: 'Fast linear rollout for low-risk MCP server config changes',
});
// Exponential: start at 5%, double every 3 minutes — more conservative than built-in
const conservativeExponential = new appconfig.CfnDeploymentStrategy(this, 'ConservativeExp', {
name: 'Exponential5PercentEvery3Minutes',
deploymentDurationInMinutes: 30,
growthFactor: 2, // Doubles the percentage each step
growthType: 'EXPONENTIAL',
finalBakeTimeInMinutes: 10,
replicateTo: 'NONE',
description: 'Conservative exponential rollout for high-risk config changes',
});
// Use a custom strategy in a deployment
new appconfig.CfnDeployment(this, 'McpConfigDeployment', {
applicationId: app.ref,
environmentId: prodEnv.ref,
configurationProfileId: profile.ref,
configurationVersion: newConfigVersion.ref,
deploymentStrategyId: fastLinearStrategy.ref,
description: 'Deploy updated rate limits with fast linear rollout',
});
The FinalBakeTimeInMinutes parameter is critical for automatic rollback: AppConfig holds at 100% deployment for this duration before transitioning the deployment to COMPLETE. If a CloudWatch alarm fires during the bake window, AppConfig rolls back automatically. If the alarm clears before the bake window ends, the deployment completes normally. Set bake time to at least 5 minutes for configs that affect tool execution paths, and 15+ minutes for security-sensitive config changes.
Setting ReplicateTo: SSM_DOCUMENT writes the strategy definition to SSM Documents, making it discoverable and reusable across multiple AWS accounts in an AWS Organization. This is useful for multi-account MCP server deployments where you want consistent rollout policies without duplicating CDK code.
Monitoring a deployment in progress
The GetDeployment API returns the current state of a deployment including PercentageComplete, DeploymentState, and timestamps. Poll this during a rollout to build a deployment progress dashboard or Slack notification:
import { AppConfigClient, GetDeploymentCommand } from '@aws-sdk/client-appconfig';
const client = new AppConfigClient({ region: process.env.AWS_REGION });
async function getDeploymentStatus(
applicationId: string,
environmentId: string,
deploymentNumber: number,
) {
const result = await client.send(
new GetDeploymentCommand({ ApplicationId: applicationId, EnvironmentId: environmentId, DeploymentNumber: deploymentNumber }),
);
return {
state: result.State, // BAKING | DEPLOYING | COMPLETE | ROLLED_BACK | ROLLING_BACK
percentComplete: result.PercentageComplete,
startedAt: result.StartedAt,
completedAt: result.CompletedAt,
eventLog: result.EventLog, // Timestamped list of state transitions
};
}
Deployment states follow this lifecycle: DEPLOYING (rollout in progress, percentage climbing) → BAKING (reached 100%, waiting for bake time to elapse) → COMPLETE (bake time elapsed, no alarm fired). If an alarm fires at any point: ROLLING_BACK → ROLLED_BACK. If you call StopDeployment manually: the current state transitions immediately to ROLLED_BACK regardless of where in the lifecycle it was.
For automated Slack notifications, create an EventBridge rule that captures AppConfig deployment state-change events and routes them to a Lambda function that posts to your Slack webhook:
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
// EventBridge rule for AppConfig deployment state changes
new events.Rule(this, 'AppConfigDeploymentRule', {
eventPattern: {
source: ['aws.appconfig'],
detailType: ['AWS AppConfig Deployment'],
detail: {
applicationId: [app.ref],
// Notify on terminal states and rollback transitions
deploymentState: ['COMPLETE', 'ROLLED_BACK', 'ROLLING_BACK'],
},
},
targets: [new targets.LambdaFunction(slackNotifierFn)],
});
The Lambda notifier receives the full deployment detail including the event log — a timestamped sequence of state transitions that shows exactly when the deployment entered each state. Include this in your Slack notification for at-a-glance incident context.
Rollback mechanics
AppConfig supports two rollback paths: automatic (alarm-triggered) and manual (StopDeployment API call).
Automatic rollback requires a CloudWatch alarm attached to the AppConfig environment's monitor configuration. When the alarm enters ALARM state during the deployment's bake window (including while the deployment is still in DEPLOYING state), AppConfig transitions to ROLLING_BACK and begins serving the previous config version to all pollers. The rollback itself uses the AllAtOnce strategy regardless of the original deployment strategy — AppConfig always reverts fast.
Manual rollback via StopDeployment works at any point before the deployment reaches COMPLETE:
import { AppConfigClient, StopDeploymentCommand } from '@aws-sdk/client-appconfig';
async function stopDeployment(
applicationId: string,
environmentId: string,
deploymentNumber: number,
) {
const client = new AppConfigClient({ region: process.env.AWS_REGION });
await client.send(
new StopDeploymentCommand({
ApplicationId: applicationId,
EnvironmentId: environmentId,
DeploymentNumber: deploymentNumber,
}),
);
console.log(`Deployment ${deploymentNumber} stopped; reverting to previous config version.`);
}
The FinalBakeTimeInMinutes window is the last opportunity for automatic rollback after the deployment reaches 100%. Once the bake time expires and the deployment transitions to COMPLETE, AppConfig considers the new config version stable. There is no automatic reverting after COMPLETE — you must start a new deployment with the previous version number if you need to revert.
After a rollback (automatic or manual), the previously-deployed config version becomes active again. AppConfig does not delete the bad version — it remains in the version history. To permanently prevent re-deployment of a bad version, mark it with a description like DO NOT REDEPLOY: caused tool errors on 2026-10-15 via the console.
AliveMCP uptime monitoring + AppConfig: complementary layers
AliveMCP's 60-second external probe interval gives you the earliest possible signal that a config change is causing MCP server failures — and that signal arrives before AppConfig's own rollback mechanisms would typically activate. Here is the timeline: a bad config deployment starts at T=0. The first 10% of Lambda containers pick up the bad config by T=45s (one poll interval). AliveMCP's probe fires at T=60s and detects that tools/list is returning an error or an unexpected tool list. AliveMCP sends an alert by T=65s. You see the alert and call StopDeployment at T=120s. AppConfig rolls back and all containers have reverted by T=165s.
Compare this with AppConfig's alarm-triggered rollback path: the CloudWatch alarm evaluation period is at minimum 1 minute, plus the alarm must sustain the breach for one evaluation period before transitioning to ALARM state. In practice, alarm-triggered rollback activates 2–8 minutes after the first failure. AliveMCP gives you a head start of 1–7 minutes. During a linear rollout, that head start means you catch the failure while only 10–30% of containers have the bad config, rather than waiting until 30–60% are affected. The bake timer hasn't expired, so StopDeployment is still available and will instantly revert all affected containers.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
Deployment is stuck in DEPLOYING state and never advances past 10% |
A CloudWatch alarm is in ALARM state when the deployment starts; AppConfig won't advance a deployment while a monitor alarm is firing |
Resolve the alarm before starting the deployment; check the deployment's event log for ALARM_TRIGGERED entries to identify which alarm is blocking progress |
| Automatic rollback does not trigger even though the CloudWatch alarm fired | The alarm was added to the AppConfig environment's monitor configuration but the IAM role attached to the monitor doesn't have cloudwatch:DescribeAlarms permission; AppConfig silently fails the check |
Verify the monitor role policy includes cloudwatch:DescribeAlarms on the specific alarm ARN; check AppConfig CloudTrail events for AccessDenied from the appconfig.amazonaws.com principal |
Deployment reached COMPLETE but you want to revert because errors appeared after bake time |
The bake window expired before errors were detected; StopDeployment is no longer available on a completed deployment |
Create a new deployment that deploys the previous config version number; retrieve the previous version's content with GetHostedConfigurationVersion and create a new version from it |
| Mixed config state: some ECS tasks have new config, others have old config, causing inconsistent tool behavior for the same client across requests | Normal behavior during a non-AllAtOnce rollout; different tasks poll at different times and pick up the new config at different points | Accept the mixed state as expected during rollout; use AllAtOnce for config changes that must be atomic across all instances; implement client-side retry logic to handle inconsistent responses |
StopDeployment returns success but config does not revert |
The deployment was already in COMPLETE state when StopDeployment was called; the API call succeeded (it's idempotent) but there's no active deployment to stop |
Check the deployment state before calling StopDeployment; if already COMPLETE, start a new deployment with the previous version instead |