Runtime Configuration

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Commands and behaviour on this page are current for AWS CLI v2 (2.37 line), the AWS AppConfig Agent 2.x (Lambda extension 2.0.22365, released 2026-08-24), AppConfig experimentation (generally available since 2026-07-01) and Spring Cloud AWS 4.2.0 (the 4.x line targets Spring Boot 4; checked 2026-10-01); re-verify against aws --version and the linked reference pages before relying on them in a pipeline. Examples use us-east-1 and the placeholder account ID 111122223333; both are stand-ins for your own values.

Most of what a running service needs to know is not code: a page size, an upstream timeout, a switch that turns a new screen on for a tenth of the traffic. This page covers the AWS ways to keep those values outside the build and, where it is worth the machinery, to change them while the service keeps running: AWS AppConfig (profiles, feature flags, safe gradual deployments, a local agent), Systems Manager Parameter Store (a plain hierarchy of values), environment variables on Lambda and ECS, and ConfigMaps on EKS. Secrets themselves — database passwords, API keys, KMS — are deliberately not repeated here; they live on Secrets Manager, Parameter Store and KMS, and this page only shows how the mechanisms below reference them. Application-side loading in Spring Boot (property-source order, @ConfigurationProperties, @RefreshScope, the Parameter Store and Secrets Manager imports) is on Configuration and Profiles; the Kubernetes objects are on ConfigMaps. For the Azure counterpart see Runtime Configuration.

The problem: configuration, secrets and flags

Bookshelf’s catalog-api (ECS), orders-api (EKS) and notifier (Lambda) need three different kinds of values, each with its own owner and change cadence:

  • Plain configuration — a log level, a page size, an upstream URL. Not sensitive, reviewable in a diff.

  • Secrets — the database credential, a third-party API key. Never plaintext at rest, access audited. Stored in Secrets Manager or as SecureString parameters and only referenced by the mechanisms on this page.

  • Feature flags — a boolean (or a value chosen per request) that turns behaviour on without a build, so a feature ships dark and is switched on for a slice of traffic, then for everyone, then removed.

All three vary per environment (dev, staging, prod), and teams increasingly want to change some of them without a redeploy. Those two wishes pull in different directions: a value baked into a task definition is trivially reviewable and reproducible but needs a rollout; a value fetched at run time changes in minutes but can break production without a pipeline noticing. AppConfig exists to give the second kind the same safety (validation, gradual rollout, automatic rollback) that a code deployment has.

Diagram of AWS AppConfig

Choosing a mechanism

Mechanism Best for Changes reach a running process Main caveat

Environment variables (Lambda, ECS task definition)

Small, stable, per-environment values such as the Spring profile or a bucket name

Never; a new function configuration or task definition revision and a rollout are required

Visible to anyone who can read the definition; not for secrets

Kubernetes ConfigMap

Non-secret settings of a pod, shared by a Deployment

Env vars: never. Mounted files: eventually, if the app re-reads them

Pods do not restart on their own when a ConfigMap changes

Parameter Store

A hierarchy of plain values per service and environment, cheap or free, plus SecureString for small secrets

Only when the code re-reads (SDK call, Spring reload); an ECS valueFrom is resolved once at task start

No validation, no staged rollout, no rollback beyond restoring an older version

AppConfig freeform profile

Structured settings (JSON, YAML) that need schema validation and a staged rollout

Within the agent poll interval after the deployment reaches the caller

An extra service to learn; a deployment is the unit of change

AppConfig feature-flag profile

Boolean and attribute-carrying flags, per-segment targeting, experiments

Same as above

Flags need a removal discipline; stale flags are technical debt

Spring spring.config.import

Loading any of the above into a Spring Environment at start

Only with an explicit reload (Parameter Store) or a restart; AppConfig import has none

Fails the start-up if a required source is missing, unless optional: is used

Rule of thumb for Bookshelf: put the Spring profile and static endpoints in the task definition or Helm values; put the page size and timeouts in Parameter Store while a single team changes them rarely; put anything that is risky to flip for everyone at once — a new recommendation algorithm, a rewritten checkout — in an AppConfig feature flag and roll it out gradually.

Parameter Store

Parameter Store is the configuration store of AWS Systems Manager: a regional key-value store whose keys are hierarchical paths, whose values are String, StringList or SecureString (encrypted with KMS), and which keeps a numbered version for every change.

Hierarchies, tiers and limits

Name parameters like a file tree so one IAM policy and one get-parameters-by-path call can cover a whole service:

/bookshelf/prod/catalog-api/page-size            String
/bookshelf/prod/catalog-api/upstream-timeout     String
/bookshelf/prod/catalog-api/db/password          SecureString   (reference only; see the secrets page)
/bookshelf/prod/orders-api/retry-max-attempts    String
/bookshelf/dev/catalog-api/page-size             String
Standard Advanced

Parameters per account and Region

10,000

100,000

Maximum value size

4 KB

8 KB

Parameter policies (expiration and change notifications)

No

Yes

Cross-account sharing

No

Yes

Cost

No additional charge

Monthly storage charge per parameter plus API interactions

Change tier later

Can be upgraded

Cannot be downgraded (delete and recreate)

Derived from Choosing parameter tiers in Parameter Store. A separate setting, throughput, controls the request rate: the default throughput is free for standard parameters, while the higher-throughput setting bills API interactions for both tiers. Pricing has three drivers — tier, API interactions (a request that returns ten parameters counts as ten) and throughput — and no number is quoted here because it moves; see the dated AWS Systems Manager pricing page.

In the AWS Management Console

  1. Pick the Region with the selector in the top bar (CloudShell opens in the same Region).

  2. Open Systems Manager → Application Management → Parameter Store → Create parameter.

  3. Name: /bookshelf/prod/catalog-api/page-size; Tier: Standard; Type: String; Data type: text; Value: 25; select Create parameter.

  4. To see history, open the parameter and choose the History tab: every change is a version, and Labels (for example approved) can be attached to a version.

  5. To browse a service, use My parameters and filter by the path prefix.

With the AWS CLI

export AWS_REGION=us-east-1

aws ssm put-parameter \
  --name /bookshelf/prod/catalog-api/page-size \
  --type String --value 25 --tier Standard

# Every parameter of one service, one request (the call is paginated; recursive walks sub-paths)
aws ssm get-parameters-by-path \
  --path /bookshelf/prod/catalog-api/ --recursive \
  --query "Parameters[].{Name:Name,Value:Value,Version:Version}" --output table

# Update (a new version), then pin a label to the version you reviewed
aws ssm put-parameter --name /bookshelf/prod/catalog-api/page-size \
  --type String --value 50 --overwrite
aws ssm label-parameter-version \
  --name /bookshelf/prod/catalog-api/page-size --parameter-version 1 --labels approved

# Read by label or by version: name:label and name:version
aws ssm get-parameter --name /bookshelf/prod/catalog-api/page-size:approved \
  --query Parameter.Value --output text
aws ssm get-parameter-history --name /bookshelf/prod/catalog-api/page-size \
  --query "Parameters[].{Version:Version,Value:Value,Labels:Labels}"

Parameter policies (Advanced tier only) add time-based behaviour: an Expiration that deletes the parameter, an ExpirationNotification that raises an EventBridge event before that, and a NoChangeNotification that fires when a parameter has not changed for a given time — useful for reminding a team to review a stale feature switch:

aws ssm put-parameter \
  --name /bookshelf/prod/catalog-api/promo-banner \
  --type String --value "spring-sale" --tier Advanced \
  --policies '[{"Type":"NoChangeNotification","Version":"1.0","Attributes":{"After":"30","Unit":"Days"}}]'

IAM policy for reading one service’s parameters

Grant the task role, Lambda execution role or Pod Identity role the narrowest read path. A SecureString also needs kms:Decrypt on its key (see the secrets page); the plain parameters below do not.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadCatalogApiParameters",
      "Effect": "Allow",
      "Action": ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"],
      "Resource": [
        "arn:aws:ssm:us-east-1:111122223333:parameter/bookshelf/prod/catalog-api",
        "arn:aws:ssm:us-east-1:111122223333:parameter/bookshelf/prod/catalog-api/*"
      ]
    }
  ]
}

The path itself is listed as well as its children because ssm:GetParametersByPath is authorised against the path that the caller passes. Derived from Restricting access to Systems Manager parameters.

AWS AppConfig

AppConfig is a capability of AWS Systems Manager built for exactly the second kind of value above. It adds what Parameter Store lacks: validation before a value is accepted, a deployment that rolls a change out gradually, automatic rollback when a CloudWatch alarm fires, first-class feature flags, and an agent that fetches and caches the result next to your code.

Concepts

Application

A namespace for one workload, here bookshelf. Not the same as an ECS service or a Lambda function.

Environment

A deployment target of an application, such as dev, staging or prod, optionally with CloudWatch alarms that guard its deployments (the monitors).

Configuration profile

The definition of one piece of configuration: where it comes from (the location) and what kind it is. A freeform profile holds JSON, YAML or text (AWS.Freeform); a feature-flag profile holds flags in the AWS.AppConfig.FeatureFlags JSON format and always lives in the hosted store.

Configuration source

Where a freeform profile’s data is stored: the AppConfig hosted configuration store (no extra cost), an S3 object, a Systems Manager parameter or document, a Secrets Manager secret, or a CodePipeline artifact. Non-hosted sources need a retrieval role that AppConfig assumes to read them.

Validator

A check run when a version is created and again at deployment time: a JSON Schema or a Lambda function that rejects a bad value before it reaches any caller. Feature-flag profiles also enforce the attribute constraints you define on each flag.

Deployment strategy

How a version rolls out: type (linear or exponential), step percentage, total deployment time and a bake time during which alarms are still watched after 100% is reached.

Deployment

One run of a strategy for a profile version into an environment. An environment runs one deployment at a time.

Deployment strategies and rollback

AppConfig ships predefined strategies and lets you create your own:

Predefined strategy Behaviour

AppConfig.Linear20PercentEvery6Minutes

AWS-recommended for production: 20% of targets every six minutes over 30 minutes, then a 30-minute bake time.

AppConfig.Canary10Percent20Minutes

AWS-recommended for production: exponential rollout with a 10% growth factor over 20 minutes, then a 10-minute bake.

AppConfig.AllAtOnce

Everything immediately, then a 10-minute bake. Quick, but alarms are your only protection.

AppConfig.Linear50PercentEvery30Seconds

Testing and demonstration only: one-minute deployment and a one-minute bake.

Derived from Using predefined deployment strategies. A linear strategy adds the step percentage evenly across the deployment time; an exponential one applies G*(2^N) (2%, 4%, 8%, …​ for a growth factor of 2). "Targets" are not instances: AppConfig counts the callers that request the configuration (agents or SDK sessions), and each one switches version when its next poll lands after its turn in the schedule.

flowchart TD A["Create hosted configuration version 7
validators run"] -->|invalid| X["Rejected, nothing deployed"] A -->|valid| B["start-deployment
strategy: AppConfig.Canary10Percent20Minutes"] B --> C["Roll out to a growing share of callers
agents poll and pick up version 7"] C --> D{"CloudWatch alarm on the environment
in ALARM state?"} D -->|yes| R["Automatic rollback
callers return to version 6"] D -->|no| E["100% reached, bake time starts"] E --> F{"Alarm during bake time?"} F -->|yes| R F -->|no| G["Deployment COMPLETE"] C -->|operator| S["stop-deployment
manual rollback"] S --> R

Rollback needs two things: an alarm attached to the environment and an IAM alarm role that lets AppConfig read the alarm state. The role trusts the AppConfig service principal and needs only cloudwatch:DescribeAlarms:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "cloudwatch:DescribeAlarms", "Resource": "*" }
  ]
}

Trust policy for the role bookshelf-appconfig-monitor:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "appconfig.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}

Create the role, the permission and the alarm (the alarm watches target 5xx responses of alb-bookshelf; the dimension value is the part of the load balancer ARN after loadbalancer/, shown here as a placeholder):

aws iam create-role --role-name bookshelf-appconfig-monitor \
  --assume-role-policy-document file://appconfig-trust.json
aws iam put-role-policy --role-name bookshelf-appconfig-monitor --policy-name describe-alarms \
  --policy-document file://describe-alarms.json

aws cloudwatch put-metric-alarm --alarm-name bookshelf-catalog-api-5xx \
  --namespace AWS/ApplicationELB --metric-name HTTPCode_Target_5XX_Count \
  --dimensions Name=LoadBalancer,Value=app/alb-bookshelf/0123456789abcdef \
  --statistic Sum --period 60 --evaluation-periods 3 --threshold 20 \
  --comparison-operator GreaterThanThreshold --treat-missing-data notBreaching

Derived from Setting up AWS AppConfig (the "Configure permissions for automatic rollback" section), aws iam create-role and aws cloudwatch put-metric-alarm.

In the AWS Management Console

  1. Pick the Region; open AWS AppConfig (listed under Systems Manager → Application Management) and choose Create application. Name it bookshelf and create it.

  2. On the application, Environments tab → Create environment: name prod; under Monitors choose the CloudWatch alarm bookshelf-catalog-api-5xx and the IAM role bookshelf-appconfig-monitor.

  3. Configuration profiles and feature flags tab → Create configuration:

    • Feature flag: give the profile a name (catalog-flags) and, on the next step, add a flag catalog-recommendations (key, name, Enabled), optionally attributes such as maxItems (number, minimum 1, maximum 50). Save and create.

    • Freeform configuration: name catalog-settings, source AWS AppConfig hosted configuration, content type JSON, paste the JSON, and add a JSON Schema validator.

  4. Open the profile and choose Start deployment: pick the environment prod, the hosted version, a strategy such as AppConfig.Canary10Percent20Minutes, and start.

  5. Watch the Deployment page: it shows the percentage, the state (DEPLOYING, BAKING, COMPLETE, ROLLED_BACK) and the alarm. Stop deployment rolls an in-progress deployment back.

With the AWS CLI

Create the application, the environment with its alarm monitor, and a freeform profile with a JSON Schema validator:

export AWS_REGION=us-east-1
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

APP_ID=$(aws appconfig create-application --name bookshelf \
  --query Id --output text)

ENV_ID=$(aws appconfig create-environment \
  --application-id "$APP_ID" --name prod \
  --monitors "[{\"AlarmArn\":\"arn:aws:cloudwatch:${AWS_REGION}:${ACCOUNT_ID}:alarm:bookshelf-catalog-api-5xx\",\"AlarmRoleArn\":\"arn:aws:iam::${ACCOUNT_ID}:role/bookshelf-appconfig-monitor\"}]" \
  --query Id --output text)

cat > catalog-settings.schema.json <<'EOF2'
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "pageSize":       { "type": "integer", "minimum": 1, "maximum": 100 },
    "upstreamTimeoutMs": { "type": "integer", "minimum": 100, "maximum": 30000 }
  },
  "required": ["pageSize", "upstreamTimeoutMs"],
  "additionalProperties": false
}
EOF2

PROFILE_ID=$(aws appconfig create-configuration-profile \
  --application-id "$APP_ID" --name catalog-settings \
  --location-uri hosted --type AWS.Freeform \
  --validators "[{\"Type\":\"JSON_SCHEMA\",\"Content\":$(jq -Rs . < catalog-settings.schema.json)}]" \
  --query Id --output text)

Save a first version of the settings (the output file receives an echo of the stored content; the version number is in the JSON that the command prints) and deploy it with a predefined strategy:

echo '{"pageSize": 25, "upstreamTimeoutMs": 2000}' > catalog-settings.json

aws appconfig create-hosted-configuration-version \
  --application-id "$APP_ID" --configuration-profile-id "$PROFILE_ID" \
  --content-type "application/json" \
  --content fileb://catalog-settings.json \
  --query VersionNumber catalog-settings.out

aws appconfig start-deployment \
  --application-id "$APP_ID" --environment-id "$ENV_ID" \
  --configuration-profile-id "$PROFILE_ID" --configuration-version 1 \
  --deployment-strategy-id AppConfig.Canary10Percent20Minutes \
  --description "page size 25"

aws appconfig list-deployments --application-id "$APP_ID" --environment-id "$ENV_ID" \
  --query "Items[].{N:DeploymentNumber,State:State,Percent:PercentageComplete}" --output table

A custom strategy, for instance a gentler production canary, and a manual rollback of the running deployment:

STRATEGY_ID=$(aws appconfig create-deployment-strategy \
  --name bookshelf-canary-10-30 \
  --deployment-duration-in-minutes 30 --growth-factor 10 --growth-type EXPONENTIAL \
  --final-bake-time-in-minutes 15 --replicate-to NONE \
  --query Id --output text)

# Roll back an in-progress deployment (the callers return to the previous version)
aws appconfig stop-deployment \
  --application-id "$APP_ID" --environment-id "$ENV_ID" --deployment-number 2

Derived from aws appconfig create-deployment-strategy and aws appconfig stop-deployment. To undo a deployment that already finished, deploy the previous version again.

Feature flags

A feature-flag profile stores flags in a defined JSON format: a flags object (metadata and attribute constraints), a values object (the current state) and a version. Attributes ride along with the flag only when it is enabled, and the constraints keep an out-of-range value from being deployed.

FLAGS_ID=$(aws appconfig create-configuration-profile \
  --application-id "$APP_ID" --name catalog-flags \
  --location-uri hosted --type AWS.AppConfig.FeatureFlags \
  --query Id --output text)

cat > catalog-flags.json <<'EOF2'
{
  "version": "1",
  "flags": {
    "catalog-recommendations": {
      "name": "Catalog recommendations",
      "attributes": {
        "maxItems": { "constraints": { "type": "number", "required": true, "minimum": 1, "maximum": 50 } }
      }
    }
  },
  "values": {
    "catalog-recommendations": { "enabled": false, "maxItems": 10 }
  }
}
EOF2

aws appconfig create-hosted-configuration-version \
  --application-id "$APP_ID" --configuration-profile-id "$FLAGS_ID" \
  --content-type "application/json" --content fileb://catalog-flags.json \
  --query VersionNumber catalog-flags.out

Derived from Creating a feature flag configuration profile (command line) and the AWS.AppConfig.FeatureFlags type reference. AppConfig validates the document against the feature-flag schema when the version is created and rejects it if an attribute breaks its constraint.

Multi-variant flags return different values for different callers: you create the profile first, then edit a flag to add variants and rules; the application passes context (a user ID, a tenant, a country) with each request and AppConfig evaluates the rules and returns the matching variant. Experimentation, generally available since 2026-07-01, builds on this: an experiment definition attaches treatments (and their traffic allocation) to a flag, the agent records which entity received which treatment, you analyse the outcome in CloudWatch or your own analytics, and the winning treatment is promoted through a normal deployment. Experiments are billed per experiment hour, and the default quotas (a limited number of experiment definitions per application, a handful of treatments per experiment) are listed under Limits and quotas.

Entity-based gradual deployments (agent 2.0.136060 or later) make a gradual rollout sticky: the caller sends an Entity-Id HTTP header (a customer ID, an account ID) and the agent consistently serves the old or the new version to that entity for the whole deployment, so one customer never flips back and forth between versions while the rollout progresses and the rollback alarm collects data. The identifier is evaluated in the agent and is not sent to the service; use a high-cardinality value, not a constant.

Retrieving configuration

Direct API retrieval uses two data-plane calls: open a session once, then poll with the token each response returns. The older GetConfiguration API is deprecated, and tutorials that use it should be updated.

TOKEN=$(aws appconfigdata start-configuration-session \
  --application-identifier bookshelf --environment-identifier prod \
  --configuration-profile-identifier catalog-settings \
  --required-minimum-poll-interval-in-seconds 60 \
  --query InitialConfigurationToken --output text)

# First call returns the content; the response carries the token for the next poll
aws appconfigdata get-latest-configuration --configuration-token "$TOKEN" settings.out

Derived from aws appconfigdata start-configuration-session and aws appconfigdata get-latest-configuration. When the content has not changed the response body is empty and only a new token is returned, so a client must keep the previous content. Calling this from every request is both slow and expensive; use the agent below, which implements the session handling, the polling and the cache for you.

Limits and quotas

Quota (default, per Region) Value

Hosted configuration size / deployment size

2,048 KB each (adjustable)

Applications per account

100

Environments per application

20

Configuration profiles per application

100

Deployment strategies

20

Experiment definitions per application

50

Treatments per experiment (excluding control)

5

GetLatestConfiguration / StartConfigurationSession

1,000 / 500 requests per second (adjustable)

Derived from AWS AppConfig endpoints and quotas; control-plane calls such as Create* and Update* are limited to a few requests per second and are not adjustable. Retrieving configuration from another AWS account is not supported by the agent’s normal path, but the agent can assume a role in the owning account (ROLE_ARN).

Pricing model

AppConfig pricing is pay-as-you-go with no minimum, on three dimensions: configuration requests (API calls that fetch configuration), configurations received (each time a caller actually receives new content), and, for experimentation, experiment hours (one experiment running for one hour, regardless of the number of targets). The hosted configuration store has no additional charge. The agent lowers the bill because it caches and polls once per host instead of once per request; a Lambda function that cold-starts often multiplies the number of fetches, which is why prefetching and a longer poll interval matter. Current prices are on the dated AWS Systems Manager pricing page.

The AppConfig Agent

The AppConfig Agent is the recommended way to read AppConfig from any compute: a small local HTTP server that calls AppConfig with the workload’s IAM role, tracks the session tokens, caches the result and refreshes it in the background. The application only does an HTTP GET on localhost:2772 (the default port, configurable) and reads from memory in milliseconds, even if the network or the service is briefly unavailable. The first call is slower because the cache is empty; prefetch removes that on start-up.

The URL shape is the same everywhere:

GET http://localhost:2772/applications/bookshelf/environments/prod/configurations/catalog-settings
GET http://localhost:2772/applications/bookshelf/environments/prod/configurations/catalog-flags?flag=catalog-recommendations
GET http://localhost:2772/applications/bookshelf/environments/prod/configurations/catalog-flags?flag=a&flag=b

A name containing a slash must be URL-encoded; response headers carry Configuration-Version and Content-Type. For a flag, the body is the flag’s state and attributes (an enabled boolean and, when enabled, the attributes). The caller needs only two IAM actions on the profile; attach this to the task role, the Lambda execution role or the Pod Identity role (not the ECS execution role, which acts before the container starts):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadBookshelfConfiguration",
      "Effect": "Allow",
      "Action": ["appconfig:StartConfigurationSession", "appconfig:GetLatestConfiguration"],
      "Resource": "arn:aws:appconfig:us-east-1:111122223333:application/APP_ID/environment/ENV_ID/configuration/PROFILE_ID"
    }
  ]
}

Derived from How to use AWS AppConfig Agent and Actions, resources, and condition keys for AWS AppConfig. Use the real IDs (not names) in the ARN; appconfig:GetLatestConfiguration and StartConfigurationSession accept a resource ARN built from the application, environment and profile IDs.

On Lambda: the extension layer

For the notifier function the agent is a Lambda extension added as a layer (named AWS-AppConfig-Extension, with an ARM64 variant). The layer ARN depends on Region and architecture and the version number changes; copy it from the extension versions page.

LAYER_ARN="<layer-arn-for-your-region-and-architecture-from-the-versions-page>"

aws lambda update-function-configuration --function-name notifier \
  --layers "$LAYER_ARN" \
  --environment "Variables={AWS_APPCONFIG_EXTENSION_PREFETCH_LIST=/applications/bookshelf/environments/prod/configurations/catalog-flags,AWS_APPCONFIG_EXTENSION_POLL_INTERVAL_SECONDS=60}"

Derived from Adding the AWS AppConfig Agent Lambda extension. In the console: Lambda → function → Layers → Add a layer → Specify an ARN. Add the two actions above to the function’s execution role. On Lambda the variables carry the prefix AWS_APPCONFIG_EXTENSION_; the useful ones are:

Variable Default Effect

AWS_APPCONFIG_EXTENSION_POLL_INTERVAL_SECONDS

45 s

How often the extension polls for updates (accepts 45, 45s, 5m, 1h).

AWS_APPCONFIG_EXTENSION_PREFETCH_LIST

none

Configurations fetched when the extension starts, before your handler’s INIT, shortening the first call.

AWS_APPCONFIG_EXTENSION_HTTP_PORT

2772

Local port.

AWS_APPCONFIG_EXTENSION_POLL_TIMEOUT_MILLIS

3000 ms

Wait for a refresh; on timeout the cached value is served.

AWS_APPCONFIG_EXTENSION_ROLE_ARN

none

Role to assume to read configuration owned by another account.

Derived from Configuring the AWS AppConfig Agent Lambda extension. Extension logs appear in the function’s CloudWatch log group, prefixed appconfig agent. The extension is runtime-agnostic. See AWS Lambda for layers, INIT and cold starts.

On ECS: a sidecar container

In an awsvpc task all containers share one network namespace, so a sidecar container reaches the application at localhost. Add a container to the catalog-api task definition (the image tag follows the 2.x line; check the documentation for the current tag):

{
  "name": "appconfig-agent",
  "image": "public.ecr.aws/aws-appconfig/aws-appconfig-agent:2.x",
  "essential": true,
  "portMappings": [ { "containerPort": 2772, "protocol": "tcp" } ],
  "environment": [
    { "name": "POLL_INTERVAL", "value": "60s" },
    { "name": "PREFETCH_LIST", "value": "bookshelf:prod:catalog-settings,bookshelf:prod:catalog-flags" },
    { "name": "SERVICE_REGION", "value": "us-east-1" }
  ],
  "logConfiguration": {
    "logDriver": "awslogs",
    "options": {
      "awslogs-group": "/ecs/bookshelf",
      "awslogs-region": "us-east-1",
      "awslogs-stream-prefix": "appconfig-agent"
    }
  }
}

Derived from Starting the AWS AppConfig agent for Amazon ECS integration. In the console: Amazon ECS → Task definitions → Create new revision → Add more containers, enter the image URI, mark it Essential and add container port 2772; then update the service to the new revision. After the deployment the container’s log shows a line such as serving on localhost:2772. The AWS documentation recommends making the application container depend on the agent being healthy; the snippet leaves out the health check, so add one that the image supports and a dependsOn condition of HEALTHY (see ECS and Fargate for the container definition and roles). The IAM actions go on the task role (bookshelf-catalog-api-task). On ECS and EKS the agent’s variables have no AWS_APPCONFIG_EXTENSION_ prefix (POLL_INTERVAL, PREFETCH_LIST, HTTP_PORT, REQUEST_TIMEOUT, ACCESS_TOKEN, LOG_LEVEL, ROLE_ARN, and so on) and the HTTP server binds to all interfaces by default, so set ACCESS_TOKEN (sent as Authorization: Bearer …​; keep the value in Secrets Manager and inject it with secrets) if anything else could reach the task’s port 2772.

On EKS: a sidecar or a DaemonSet

On EKS the same container goes into the pod of orders-api, next to the application container; the pod’s service account is associated with an IAM role through EKS Pod Identity (see Deploying Microservices on EKS) and that role carries the two AppConfig actions:

spec:
  serviceAccountName: orders-api
  containers:
    - name: orders-api
      image: 111122223333.dkr.ecr.us-east-1.amazonaws.com/bookshelf/orders-api:1.0.0
      env:
        - name: APPCONFIG_URL
          value: http://localhost:2772/applications/bookshelf/environments/prod/configurations
    - name: appconfig-agent
      image: public.ecr.aws/aws-appconfig/aws-appconfig-agent:2.x
      ports:
        - name: http
          containerPort: 2772
          protocol: TCP
      env:
        - name: SERVICE_REGION
          value: us-east-1
        - name: POLL_INTERVAL
          value: 60s
      imagePullPolicy: IfNotPresent

Derived from Starting the AWS AppConfig agent for Amazon EKS integration. Check it with kubectl logs -n bookshelf <pod> -c appconfig-agent. A sidecar costs one extra container per pod; the alternative is a DaemonSet with one agent per node. A DaemonSet agent runs in a separate pod, so the application cannot call localhost: you must inject or discover the agent pod’s address, which is more moving parts but fewer agents polling (see Running AWS AppConfig as a DaemonSet in Amazon EKS). For Fargate pods the pod execution role is not the place for the AppConfig actions; use a Pod Identity or IRSA role on the service account as for any other AWS access.

Reading a flag from Java

One small client serves all three runtimes because the URL is identical. It uses only the JDK’s HttpClient; the agent already caches, so no cache is needed in the application, and a short timeout plus a safe default keeps a flag problem from taking the service down:

package com.example.bookshelf.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class FlagClient {

    private final HttpClient http = HttpClient.newBuilder()
            .connectTimeout(Duration.ofMillis(200)).build();
    private final ObjectMapper mapper = new ObjectMapper();
    private final String baseUrl; // http://localhost:2772/applications/bookshelf/environments/prod/configurations

    public FlagClient(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /** Returns the flag's enabled state, or {@code fallback} if the agent cannot answer. */
    public boolean isEnabled(String profile, String flag, String entityId, boolean fallback) {
        try {
            HttpRequest.Builder request = HttpRequest.newBuilder()
                    .uri(URI.create(baseUrl + "/" + profile + "?flag=" + flag))
                    .timeout(Duration.ofMillis(500));
            if (entityId != null) {
                request.header("Entity-Id", entityId); // sticky rollout per customer
            }
            HttpResponse<String> response =
                    http.send(request.build(), HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() != 200) {
                return fallback;
            }
            JsonNode body = mapper.readTree(response.body());
            return body.path("enabled").asBoolean(fallback);
        } catch (Exception e) {
            return fallback;
        }
    }
}

Derived from Retrieving configuration data for applications running in Amazon ECS and Amazon EKS. For the direct-SDK alternative use the AWS SDK for Java 2.x AppConfigDataClient (the software.amazon.awssdk:appconfigdata artifact) with the default credential chain; it is what the Spring starter below uses.

Spring Cloud AWS 4 and AppConfig

Spring Cloud AWS added AppConfig as a configuration source in 4.1.0 (2026-07-22); it is not available in the 3.x line that targets Spring Boot 3.5, so a Boot 3 application uses the agent above or the SDK directly. The existing page AWS Secrets Manager and Parameter Store covers the Parameter Store and Secrets Manager imports; this section adds only what is specific to AppConfig. Add the starter from the Spring Cloud AWS BOM (4.2.0 is the current release):

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.awspring.cloud</groupId>
      <artifactId>spring-cloud-aws-dependencies</artifactId>
      <version>4.2.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependency>
  <groupId>io.awspring.cloud</groupId>
  <artifactId>spring-cloud-aws-starter-app-config</artifactId>
</dependency>

The import key names the application, the profile and the environment, separated by # (all three are required, as names or IDs):

spring:
  config:
    import:
      - "optional:aws-app-config:bookshelf#catalog-settings#prod"
  cloud:
    aws:
      region:
        static: us-east-1

The loader supports JSON, YAML and properties-style freeform profiles; pageSize: 25 in the profile becomes the property pageSize. Without optional: the application refuses to start if the profile cannot be loaded, which is usually what you want in prod and not in a developer’s laptop profile. The starter registers an AppConfigDataClient bean (customisable with an AppConfigClientCustomizer) and honours spring.cloud.aws.appconfig.enabled, .endpoint, .region and .separator. Credentials come from the default chain, so the task role or Pod Identity role needs appconfig:StartConfigurationSession and appconfig:GetLatestConfiguration on the profile.

@Component
public class CatalogSettings {

    private final int pageSize;

    public CatalogSettings(@Value("${pageSize}") int pageSize) {
        this.pageSize = pageSize;
    }

    public int pageSize() {
        return pageSize;
    }
}

Derived from the Spring Cloud AWS reference documentation (AppConfig integration) and the 4.1.0 release notes. Two limits matter. First, the import reads the configuration at start-up and the AppConfig loader documents no reload strategy of its own, unlike Parameter Store’s. Second, an AppConfig deployment strategy rolls out gradually to the callers that poll; a single start-up read is one caller at one moment, so the gradual rollout and the alarm rollback protect only the instances that start during the deployment. For values that must change under a running service, either let the agent and FlagClient above serve them (no restart, gradual rollout honoured), or restart the service on a deployment (an EventBridge rule on the AppConfig deployment events can trigger an ECS update-service --force-new-deployment or a kubectl rollout restart).

Parameter Store has an opt-in reload in the same library: set spring.cloud.aws.parameterstore.reload.strategy to refresh (re-bind @ConfigurationProperties and @RefreshScope beans; needs Actuator and Spring Cloud Commons) or restart_context, with a polling period that defaults to one minute. That is a polling alternative to the EventBridge-triggered refresh described on the existing configuration page.

Environment variables

Environment variables are the simplest mechanism and the least dynamic: a value is read when the process starts.

Lambda. Variables belong to the function configuration and a new version or $LATEST update gives new execution environments the new values; running environments are replaced as they recycle. The total size is limited (4 KB) and the values are encrypted at rest with a Lambda-managed key, or a customer managed KMS key if policy requires it; do not put secrets there.

aws lambda update-function-configuration --function-name notifier \
  --environment "Variables={SPRING_PROFILES_ACTIVE=aws,LOG_LEVEL=info,CATALOG_URL=https://catalog.example.com}"

Derived from aws lambda update-function-configuration. Console: Lambda → function → Configuration → Environment variables → Edit.

ECS. Variables belong to a container in a task definition revision (environment for plain values, secrets with valueFrom for Secrets Manager and Parameter Store, resolved once at task start by the execution role, see ECS and Fargate). Changing one means registering a new revision and updating the service, which performs a rolling or blue/green deployment (see ECS Deployments, Scaling and Networking):

aws ecs update-service --cluster bookshelf --service catalog-api \
  --task-definition catalog-api:8 --force-new-deployment

Derived from aws ecs update-service. A Spring Boot application maps SPRING_PROFILES_ACTIVE and relaxed names such as BOOKSHELF_PAGE_SIZE onto properties, and environment variables outrank the packaged and imported files (see Precedence: which value wins).

Kubernetes ConfigMaps on EKS

A ConfigMap is a Kubernetes object holding non-secret key-value data; the object itself is explained on ConfigMaps, the Spring-side watch-and-reload option on Kubernetes ConfigMaps and Secrets, and the cluster basics on Orchestration: Swarm Mode and Kubernetes. This section covers what is specific to running it on EKS.

apiVersion: v1
kind: ConfigMap
metadata:
  name: catalog-api-config
  namespace: bookshelf
data:
  SPRING_PROFILES_ACTIVE: aws
  BOOKSHELF_PAGE_SIZE: "25"
  application-aws.yml: |
    bookshelf:
      uploads-bucket: bookshelf-uploads-111122223333
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-api
  namespace: bookshelf
spec:
  replicas: 2
  selector:
    matchLabels: { app: orders-api }
  template:
    metadata:
      labels: { app: orders-api }
      annotations:
        checksum/config: "set-by-ci-to-the-hash-of-the-configmap"
    spec:
      containers:
        - name: orders-api
          image: 111122223333.dkr.ecr.us-east-1.amazonaws.com/bookshelf/orders-api:1.0.0
          envFrom:
            - configMapRef:
                name: catalog-api-config
          volumeMounts:
            - name: config
              mountPath: /config
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: catalog-api-config
            items:
              - key: application-aws.yml
                path: application-aws.yml

How a change behaves:

  • As environment variables (env, envFrom): fixed when the container starts. Editing the ConfigMap changes nothing in a running pod; the pods must be recreated.

  • As a mounted volume: the kubelet refreshes the files eventually (on its sync period plus its cache delay, so expect on the order of a minute), but not for a subPath mount, and the application must re-read the file. Spring does not re-read a mounted file by itself unless the Kubernetes config client or a file watcher is used.

  • Immutable ConfigMaps (immutable: true) cannot change at all; the update is a new ConfigMap name and a new Deployment revision, which gives a clean rollback.

  • Rolling out a change: a Deployment does not restart when a ConfigMap changes. Either run kubectl rollout restart deployment/orders-api -n bookshelf, or make the pod template change when the data does, as the checksum/config annotation above does (Helm templates compute it with sha256sum; see Deploying Microservices on EKS for the chart and rollout handling).

Generating a ConfigMap from Parameter Store keeps one source of truth without an extra controller; a CI step or a Helm pre-install hook is enough:

aws ssm get-parameters-by-path --path /bookshelf/prod/orders-api/ --recursive \
  --query "Parameters[].[Name,Value]" --output text \
| awk -F'\t' '{ n=split($1, p, "/"); print toupper(gensub(/-/, "_", "g", p[n])) "=" $2 }' > orders-api.env

kubectl create configmap orders-api-config -n bookshelf \
  --from-env-file=orders-api.env --dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart deployment/orders-api -n bookshelf

Derived from aws ssm get-parameters-by-path and Kubernetes ConfigMaps. (gensub is GNU awk; on macOS install gawk or use sed.) For secret values the supported EKS patterns are the Secrets Store CSI Driver with the AWS provider (ASCP), which mounts them as files and can sync them to a Kubernetes Secret, and the External Secrets Operator; both are on Secrets Manager, Parameter Store and KMS, not repeated here. ASCP’s sync produces Secrets, not ConfigMaps, so for plain values prefer the CI generation above, the AppConfig Agent in the pod (previous section), or the Spring Kubernetes reload. The agent is usually the best fit when a change should reach running pods without any restart.

Precedence: which value wins

Two different precedence questions exist. Within one Spring process, values come from property sources ranked by Spring Boot; the table lists them from highest to lowest for a Bookshelf service on AWS (the full order is on the Spring configuration page):

Rank Source Notes

Highest

Command-line arguments (an ECS container command, a Kubernetes args)

Rare in practice; useful for a one-off override.

High

OS environment variables: Lambda Variables, ECS environment, envFrom of a ConfigMap

Relaxed binding maps BOOKSHELF_PAGE_SIZE to bookshelf.page-size.

Middle

Imported sources: aws-app-config:, aws-parameterstore:, aws-secretsmanager:

An imported document overrides the file that imports it; among several imports, later entries win.

Lower

Profile-specific files (application-aws.yml, a mounted /config/application-aws.yml)

Selected by SPRING_PROFILES_ACTIVE.

Lowest

application.yml in the jar and defaults in code

Always present, so a missing remote source degrades gracefully if it was optional:.

The second question is outside the property system: a feature flag read from the agent (FlagClient) is not a property source and is not overridden by any of these; the code decides what it does when the agent is unreachable (the fallback argument). Do not define the same logical setting in two places; if you must, document which wins, and remember that a Parameter Store valueFrom in an ECS task definition and an environment entry with the same name are two sources of one variable, a conflict to avoid rather than to rely on.

Refresh strategies

Strategy How it works Typical delay and cost

Redeploy on change

A pipeline or an EventBridge rule on the parameter or deployment event runs update-service --force-new-deployment or kubectl rollout restart

Minutes; one rolling restart per change; simplest to reason about

Agent poll (AppConfig)

The agent polls every POLL_INTERVAL (45 s by default) and serves from its cache

Up to one poll interval after the deployment reaches the caller; low API cost

Spring reload (Parameter Store)

spring.cloud.aws.parameterstore.reload.strategy re-reads on a period (default one minute) and refreshes @ConfigurationProperties and @RefreshScope beans

About the period; API calls per instance per period

EventBridge-triggered /actuator/refresh

A rule on a Parameter Store change event calls the refresh endpoint of each instance

Seconds; needs a safe, authenticated call path to every instance

Application-level cache with TTL

The code calls the SDK (Parameter Store GetParameter) and keeps the value for N seconds

The TTL; watch the Parameter Store throughput limit

A caution on rollouts: every strategy except the agent changes all instances at about the same time. If a bad value can hurt, put it behind AppConfig, where the deployment strategy and the alarm provide the gradual rollout and the automatic rollback.

The Bookshelf example: rolling out a flag gradually

Goal: turn on catalog-recommendations for catalog-api in production, without a redeploy, safely.

  1. Deploy catalog-api once with the agent sidecar, the FlagClient and the task-role permissions from the previous sections. The code calls flags.isEnabled("catalog-flags", "catalog-recommendations", customerId, false), so the default is off and an unreachable agent is also off.

  2. In the bookshelf application, make sure the prod environment has the alarm bookshelf-catalog-api-5xx (an Application Load Balancer target 5xx count above a threshold) and the monitor role.

  3. Create a new hosted version of catalog-flags with "enabled": true and "maxItems": 10. The attribute constraint rejects a value outside 1 to 50 before anything deploys.

  4. Start the deployment with AppConfig.Canary10Percent20Minutes (or bookshelf-canary-10-30): about 10% of the agents switch first, then the share grows; because the code passes Entity-Id, each customer sees a consistent state.

  5. Watch the deployment and the alarm. If errors rise, the alarm moves to ALARM, AppConfig rolls back, and the flag returns to off everywhere with no release. Otherwise the deployment completes after the bake time.

  6. Once the feature is permanent, delete the flag from the profile and the call from the code.

aws appconfig start-deployment \
  --application-id "$APP_ID" --environment-id "$ENV_ID" \
  --configuration-profile-id "$FLAGS_ID" --configuration-version 2 \
  --deployment-strategy-id AppConfig.Canary10Percent20Minutes \
  --description "enable catalog-recommendations"

Security and networking

  • Least privilege. Read-only roles for workloads (the two data-plane actions on one profile, ssm:GetParametersByPath on one path); a separate pipeline role for appconfig:CreateHostedConfigurationVersion and appconfig:StartDeployment, so that changing production configuration is an auditable, reviewable act. All calls are recorded in CloudTrail; tag applications and use aws:ResourceTag conditions for team boundaries.

  • Encryption. Parameter Store SecureString and Secrets Manager use KMS (see the secrets page). Hosted AppConfig data is encrypted at rest; use a customer managed key via the profile’s KMS key setting if policy requires.

  • Never put secrets in a flag or a freeform profile. Reference them (a Secrets Manager secret as a profile source, or an ID the application resolves) rather than copy them.

  • Private networking. Workloads in private subnets reach the services through a NAT gateway or through VPC interface endpoints for Systems Manager (ssm) and AppConfig (appconfig, appconfigdata); see VPC Networking. The agent talks only to the AppConfig data plane, not to the application, so there is no inbound rule to open beyond the application’s own port.

  • Agent exposure. On ECS and EKS the agent binds to all interfaces by default; set ACCESS_TOKEN or HTTP_HOST to restrict it.

What changed recently, and what older guides still show

  • AppConfig is documented as a capability of AWS Systems Manager, and many older tutorials still point to a separate "AppConfig" console or to the AWS AppConfig Agent under its earlier name "AppConfig Lambda extension". The agent now covers Lambda, ECS, EKS and EC2.

  • Older guides call the deprecated GetConfiguration API; current code uses StartConfigurationSession and GetLatestConfiguration, or the agent.

  • AppConfig experimentation (A/B testing on flags) became generally available on 2026-07-01, with agent support for assignment logging.

  • Entity-based gradual deployments (agent 2.0.136060 and later) make rollouts sticky per customer.

  • Spring Cloud AWS had no AppConfig support before 4.1.0 (2026-07-22); older Spring tutorials hand-roll an AppConfigDataClient or poll a @Scheduled method. Parameter Store starters were renamed from spring-cloud-starter-aws-parameter-store-config to spring-cloud-aws-starter-parameter-store.

  • Systems Manager’s own CloudWatch dashboard was retired after 2026-04-30; use the CloudWatch console.

Troubleshooting

Symptom Likely cause and fix

Agent log shows AccessDenied on StartConfigurationSession

The role lacks the two AppConfig actions, or the resource ARN uses names instead of IDs. On Lambda it is the execution role, on ECS the task role, on EKS the Pod Identity role (verify the association and the service account).

localhost:2772 refuses connections on ECS

The agent container is not running or not in the same task; check the task’s container list and the agent log line serving on localhost:2772. Start order matters on first call; prefetch helps.

Value never changes after a deployment

The deployment is still DEPLOYING or BAKING, the agent poll interval has not elapsed, or a different environment or profile is being read. Check list-deployments, the Configuration-Version header and POLL_INTERVAL.

Deployment rolled back by itself

The environment’s alarm entered ALARM during deployment or bake time. Read the alarm history; fix the cause or the alarm, and redeploy.

BadRequestException creating a version

A validator rejected the data (JSON Schema, Lambda, or a flag attribute constraint); the message names the violation.

First request after a cold start is slow

An empty agent cache. Add PREFETCH_LIST and a longer poll interval.

Spring app fails at start with an AppConfig error

Import key must name application, profile and environment separated by #; a missing profile fails start unless optional: is used; credentials must come from the default chain.

ConfigMap edited but pods still use old values

Env-var consumers never see changes; restart the Deployment or change the checksum annotation. Volume subPath mounts never refresh.

ECS task will not start: ResourceInitializationError on a parameter

The execution role cannot read the Parameter Store path (or the KMS key for a SecureString), or the Region differs.

ThrottlingException from Parameter Store

Too many reads per second; cache in the application, use GetParametersByPath, or enable higher throughput.

Clean up

Delete in dependency order. A deployment in progress must finish or be stopped before its environment is deleted.

# AppConfig: deployment strategy, profiles' hosted versions, profiles, environment, application
aws appconfig delete-deployment-strategy --deployment-strategy-id "$STRATEGY_ID"
for v in 1 2; do
  aws appconfig delete-hosted-configuration-version \
    --application-id "$APP_ID" --configuration-profile-id "$PROFILE_ID" --version-number "$v"
  aws appconfig delete-hosted-configuration-version \
    --application-id "$APP_ID" --configuration-profile-id "$FLAGS_ID" --version-number "$v"
done
aws appconfig delete-configuration-profile --application-id "$APP_ID" --configuration-profile-id "$PROFILE_ID"
aws appconfig delete-configuration-profile --application-id "$APP_ID" --configuration-profile-id "$FLAGS_ID"
aws appconfig delete-environment --application-id "$APP_ID" --environment-id "$ENV_ID"
aws appconfig delete-application --application-id "$APP_ID"

# Parameter Store
aws ssm delete-parameters --names \
  /bookshelf/prod/catalog-api/page-size /bookshelf/prod/catalog-api/promo-banner

# Alarm role and agent wiring
aws iam delete-role-policy --role-name bookshelf-appconfig-monitor --policy-name describe-alarms
aws iam delete-role --role-name bookshelf-appconfig-monitor
aws cloudwatch delete-alarms --alarm-names bookshelf-catalog-api-5xx

# Kubernetes
kubectl delete configmap catalog-api-config orders-api-config -n bookshelf

Derived from aws appconfig delete-application, aws appconfig delete-environment, aws appconfig delete-configuration-profile, aws appconfig delete-hosted-configuration-version, aws ssm delete-parameters and aws iam delete-role. A configuration profile cannot be deleted while it still has hosted versions, and an environment or profile in use by an active deployment cannot be deleted. In the console: AppConfig → application → delete the environment and each profile, then Delete application; Parameter Store → select the parameters → Delete; remove the extension layer from the notifier function (Layers → Edit), the agent container from the task definition (a new revision) and from the pod spec. The alarm deletion above applies only if it was created for this exercise. Label versions go with their parameter. Remove the agent’s IAM statements from the task, execution and Pod Identity roles when you retire the feature.