diff --git a/config/changelog.example.yml b/config/changelog.example.yml
index 8c62b4e4c..c738ecbfe 100644
--- a/config/changelog.example.yml
+++ b/config/changelog.example.yml
@@ -234,11 +234,15 @@ bundle:
   # Used by the {changelog} directive to generate correct PR/issue links when the product ID
   # differs from the GitHub repository name. Can be overridden per profile.
   # repo: elasticsearch
-  # Optional: default GitHub owner applied to all profiles that do not specify their own.
+  # Optional: default GitHub owner applied to all profiles that do not specify their own. Can be overridden per profile.
   # owner: elastic
   # Optional: branch whose CDN changelog pool (changelog/{org}/{repo}/{branch}/...) is sourced from when
   # bundling entries from the CDN. Defaults to "main" when unset. Can be overridden per profile.
   # branch: main
+  # Optional: source changelog entries from the local directory instead of the public CDN.
+  # Default (false)
+  # Set to true only when you need local-only bundling (or use --force-local)
+  # use_local_changelogs: true
   # Optional: control auto-population of release-date for all profiles by default.
   # When true (default), auto-populate release dates. Profiles can override this setting.
   # release_dates: true
diff --git a/docs/cli/changelog/cmd-bundle.md b/docs/cli/changelog/cmd-bundle.md
index 79a1c28d7..136d32f6a 100644
--- a/docs/cli/changelog/cmd-bundle.md
+++ b/docs/cli/changelog/cmd-bundle.md
@@ -15,6 +15,7 @@ docs-builder changelog bundle elasticsearch-release 9.2.0 ./promotion-report.htm
 ```
 
 The second positional argument accepts:
+
 - A version string (e.g. `9.2.0`, `9.2.0-beta.1`) — lifecycle is inferred automatically (`ga`, `beta`, `rc`)
 - A promotion report URL or file path
 - A plain-text URL list file (one fully-qualified GitHub PR or issue URL per line)
@@ -147,6 +148,26 @@ Set `bundle.link_allow_repos` in `changelog.yml` to an explicit list of `owner/r
 `bundle.link_allow_repos` requires a **resolved** bundle. Set `bundle.resolve: true` or pass `--resolve`.
 :::
 
+## Entry sourcing [changelog-bundle-entry-sourcing]
+
+You can choose to pull changelog files from the local folder or from the public CDN.
+
+Files in the local folder are used when:
+
+- `bundle.use_local_changelogs` is set to `true` in the changelog configuration file,
+- the `--force-local`, `--files`, or `--directory` command options are used,
+- a path-list file is used with profile-based `changelog bundle` commands, or
+- the repo cannot be determined (`bundle.repo` is not set and `--repo` is not specified)
+
+Files in the public CDN are used when the repo is known (`bundle.repo` is set in the changelog configuration file or the `--repo` command option is used) and local sourcing is not forced.
+
+:::{note}
+The public CDN (CloudFront) caches changelog entry YAML and the entry `registry.json` with a default "time to live" (TTL) of about one hour (minimum 60 seconds).
+After you upload or edit entries, the copy in the private S3 bucket can be newer than what `changelog bundle` downloads from the CDN.
+If you rely on CDN sourcing, wait at least an hour after last-minute changelog updates before bundling.
+Alternatively, if changelogs are also stored in the repo, you can force local entry sourcing so the command reads files locally instead.
+:::
+
 ## Examples
 
 ### Bundle by GitHub release [changelog-bundle-release-version]
@@ -352,18 +373,6 @@ docs-builder changelog bundle serverless-release 2026-07-07 ./docs/temp/changelo
 
 `--files` / path-list selection always reads the named files from disk (local entry sourcing). It does not fetch entries from the CDN. `rules.bundle` still applies after selection.
 
-### Force local entry sourcing [changelog-bundle-force-local]
-
-When a repository defaults to CDN entry sourcing, you can use `--force-local` to read changelog YAML files from the local folder.
-This option overrides the `bundle.use_local_changelogs` setting in your `changelog.yml` and is useful for ad hoc bundles that include freshly authored local files that are not on the CDN yet.
-
-```sh
-docs-builder changelog bundle serverless-release 2026-07-07 ./docs/temp/prs.txt --force-local
-```
-
-`--force-local` is allowed in both option-based and profile-based commands.
-Path-list / `--files` filters already force local sourcing, so `--force-local` is optional in that case.
-
 ### Hide features [changelog-bundle-hide-features]
 
 You can use the `--hide-features` option to embed feature IDs that should be hidden when the bundle is rendered. This is useful for features that are not yet ready for public documentation.
diff --git a/docs/contribute/bundle-changelogs.md b/docs/contribute/bundle-changelogs.md
index 5802a2c78..440fe45f6 100644
--- a/docs/contribute/bundle-changelogs.md
+++ b/docs/contribute/bundle-changelogs.md
@@ -38,6 +38,18 @@ For example, you can configure [rules](/contribute/create-changelogs.md#rules) t
 Your release workflow should not assume there will be a one-to-one mapping between what was shipped and what will be documented.
 :::
 
+## Identify where to find changelogs
+
+As noted in the [changelog creation step](/contribute/create-changelogs.md#prerequisites), you can store your changelog files in both the public CDN and your GitHub repository.
+
+By default, when you create a bundle the commands and GitHub actions pull the changelogs from the public CDN.
+
+If you're making last-minute changes and ad hoc releases with changelog files that aren't uploaded to the public CDN yet, pass the `--force-local` command option to `changelog bundle`.
+
+If you rely on CDN sourcing, wait at least an hour after your final changelog updates before you create the bundle.
+
+For more details, refer to [Entry sourcing](/cli/changelog/bundle.md#changelog-bundle-entry-sourcing).
+
 ## Create profiles
 
 The [changelog bundle](/cli/changelog/bundle.md) command has two modes of operation.
diff --git a/docs/contribute/configure-changelogs-ref.md b/docs/contribute/configure-changelogs-ref.md
index bdb7a6e48..474d716c9 100644
--- a/docs/contribute/configure-changelogs-ref.md
+++ b/docs/contribute/configure-changelogs-ref.md
@@ -46,15 +46,15 @@ These settings are relevant to one or all of the `changelog bundle`, `changelog
 
 | Setting                   | Description |
 | ------------------------- | ----------- |
-| `bundle.branch`           | Branch whose CDN changelog pool (`changelog/{org}/{repo}/{branch}/...`) entries are sourced from when bundling (default: `main`). Refer to [Entry sourcing](#bundle-entry-sourcing). |
+| `bundle.branch`           | Branch whose CDN changelog pool (`changelog/{org}/{repo}/{branch}/...`) entries are sourced from when bundling (default: `main`). Refer to [Entry sourcing](/cli/changelog/bundle.md#changelog-bundle-entry-sourcing). |
 | `bundle.directory`        | Input directory containing changelog YAML files (default: `docs/changelog`). |
 | `bundle.link_allow_repos` | List of `owner/repo` pairs whose PR/issue links are preserved. When set (including empty `[]`), links to unlisted repos become `# PRIVATE:` sentinels. Requires `bundle.resolve: true` |
 | `bundle.output_directory` | Output directory for bundled files (default: `docs/releases`). |
 | `bundle.owner`            | Default GitHub repository owner (for example, `elastic`). Also the org segment of uploaded changelog-entry keys (`changelog/{org}/{repo}/{branch}/...`) and CDN entry sourcing. |
 | `bundle.release_dates`    | When `true`, bundles include a `release-date` field (default: true). |
-| `bundle.repo`             | Default GitHub repository name (for example, `elasticsearch`). Used by the `{changelog}` directive to generate correct PR and issue links, and to scope uploaded changelog-entry keys (`changelog/{org}/{repo}/{branch}/...`) and CDN entry sourcing. Only needed when the product ID doesn't match the GitHub repository name (or to override the git remote). |
+| `bundle.repo`             | Default GitHub repository name (for example, `elasticsearch`). Used by the `{changelog}` directive to generate correct PR and issue links, to scope uploaded changelog file locations (`changelog/{org}/{repo}/{branch}/...`), and for CDN entry sourcing when bundling. Only needed when the product ID doesn't match the GitHub repository name. Refer to [Entry sourcing](/cli/changelog/bundle.md#changelog-bundle-entry-sourcing). |
 | `bundle.resolve`          | When `true`, changelog contents are copied into bundle (default: `true`). |
-| `bundle.use_local_changelogs` | When `true`, always source entries from the local folder and never from the CDN (default: `false`). Refer to [Entry sourcing](#bundle-entry-sourcing). |
+| `bundle.use_local_changelogs` | When `true`, always source entries from the local folder and never from the CDN (default: `false`). Refer to [Entry sourcing](/cli/changelog/bundle.md#changelog-bundle-entry-sourcing). |
 
 :::
 
@@ -65,21 +65,6 @@ When `bundle.link_allow_repos` is omitted, no link filtering occurs.
 - For public repos, add your `owner/repo` to the list at a minimum.
 :::
 
-### Entry sourcing [bundle-entry-sourcing]
-
-`changelog bundle` reads the individual changelog entries it aggregates either from the local folder or from the public CDN. Entries are stored on the CDN per **authoring org/repo/branch** (`changelog/{org}/{repo}/{branch}/...`), not per product, so CDN sourcing keys off the resolvable authoring org/repo/branch rather than the bundle's target products.
-
-The authoring repo is resolved with the same precedence as `changelog upload`: `--repo` > `bundle.repo` in `changelog.yml` > the git remote origin. The owner is resolved from `--owner` > `bundle.owner` (default `elastic`), and the branch from `--branch` > `bundle.branch` (default `main`).
-
-Sourcing is decided per run:
-
-- **Local folder.** Used when `bundle.use_local_changelogs: true`, when `--force-local` is passed, when `--files` / a path-list filter is used, when `--directory` is passed, or when the authoring repo cannot be resolved. The folder must contain the changelog files.
-- **CDN (default when a repo resolves).** Used when the authoring repo resolves, local sourcing is not forced, and a CDN base URL is configured (`DOCS_BUILDER_CHANGELOG_CDN`, defaulting to the public distribution). The command fetches `changelog/{org}/{repo}/{branch}/registry.json` and the entries it lists, then applies the bundle's own product/PR/issue filters to the downloaded set.
-
-Use `--force-local` for uncommon ad hoc runs that need the local folder without editing `changelog.yml`. Path-list / `--files` filters always force local sourcing because they select files by path on disk.
-
-Because entries are org/repo/branch-scoped, one repository can produce a bundle for a shared product (for example, `cloud-serverless`) while sourcing its own entries from `changelog/{org}/{repo}/{branch}/`, without that product appearing in the repository's `docset.yml`. The `{changelog}` directive's `:cdn:` mode still consumes product-scoped *bundles*, so a repository that also renders its own release notes declares each product under `release_notes` as before.
-
 ### Bundle descriptions [bundle-descriptions]
 
 You can add introductory text to bundles using the `description` field. This text appears at the top of rendered changelogs, after the release heading but before the entry sections.
@@ -117,6 +102,9 @@ They work with both `changelog bundle` and `changelog remove` commands.
 
 These settings are located in the `bundle.profiles.<name>` section of the configuration file:
 
+`branch`:
+:   Overrides the global [bundle.branch](#bundle-basic).
+
 `description`
 :   Overrides the global [bundle.description](#bundle-descriptions).
 
diff --git a/docs/contribute/create-changelogs.md b/docs/contribute/create-changelogs.md
index 22c750a96..d2660cb61 100644
--- a/docs/contribute/create-changelogs.md
+++ b/docs/contribute/create-changelogs.md
@@ -5,11 +5,16 @@ These files are ultimately used to generate release documentation.
 
 This page describes how to create these files both from the [command line](#command-line) and from [GitHub actions](#github-actions).
 
-## Before you begin
+## Before you begin [prerequisites]
 
 Create a changelog configuration file to define all the default behavior and PR label mappings.
 Refer to [](/contribute/configure-changelogs.md).
 
+Choose whether you want to store the changelogs only in the public CDN or in both your GitHub repository and the CDN:
+
+- If you have a large volume of notable changes every release, you will likely choose to store them *only* in the public CDN.
+- If you have a small volume of changes or the content churns a lot before and after releases, you will likely choose to store them in *both* the repo and the public CDN.
+
 ## Create changelog files from command line [command-line]
 
 These steps describe how to use the [changelog add](/cli/changelog/add.md) command.
@@ -93,6 +98,25 @@ Some of the fields in the schema accept only a specific set of values:
 You can further limit the possible values with the [products](/contribute/configure-changelogs-ref.md#products) and [lifecycles](/contribute/configure-changelogs-ref.md#lifecycles) options in the changelog configuration file.
 :::
 
+## Upload changelog files [upload]
+
+After you're satisfied with the content of your changelog files, the last step is to upload them to the public CDN.
+
+:::{important}
+All changelog files should be uploaded to the public CDN, irrespective of whether you also want to maintain a copy in your GitHub repo.
+:::
+
+- If you create changelog files from the command line, use the [changelog upload](/cli/changelog/upload.md) command to upload them.
+- If you create your changelogs from GitHub actions, add an upload workflow. Refer to [Uploading to S3](https://github.com/elastic/docs-actions/blob/main/changelog/README.md#uploading-to-s3) and [changelog-upload.yml](https://github.com/elastic/docs-actions/blob/main/.github/workflows/changelog-upload.yml).
+
+Files land in a private bucket, which is the internal source of truth.
+A scrubber Lambda automatically mirrors sanitized copies (with private repository references removed) to the public bucket.
+Changelog entries are uploaded into `changelog/{org}/{repo}/{branch}/{filename}`.
+
+:::{note}
+It can take between 60 seconds to an hour before updates appear in the public CDN.
+:::
+
 ## Examples
 
 ### Control changelog creation [rules]
diff --git a/docs/development/changelog-bundle-registry.md b/docs/development/changelog-bundle-registry.md
index 2f3072715..a2fe3ea83 100644
--- a/docs/development/changelog-bundle-registry.md
+++ b/docs/development/changelog-bundle-registry.md
@@ -182,9 +182,11 @@ entries from the local folder or fetch the **authoring pool's** published entrie
 `CdnChangelogEntryFetcher`).
 
 Under the artifact-root layout, entries are org/repo/branch-scoped — not product-scoped — so CDN
-entry sourcing keys off the resolvable authoring pool (repo with the same precedence as upload:
-`--repo` > `bundle.repo` > git remote; owner from `--owner` > `bundle.owner`, default `elastic`;
-branch from `--branch` > `bundle.branch`, default `main`), **not** off the bundle's target products.
+entry sourcing keys off the resolvable authoring pool (`--repo` > profile `repo` >
+`bundle.repo`; owner from `--owner` > profile `owner` > `bundle.owner`, default `elastic`;
+branch from `--branch` > profile `branch` > `bundle.branch`, default `main`). Git remote and
+checkout branch are **not** used for bundle entry sourcing (unlike `changelog upload`). The pool
+is keyed by authoring org/repo/branch, **not** by the bundle's target products.
 This is what lets one repo (for example `kibana`) produce a bundle for a shared product (for example
 `cloud-serverless`) while sourcing its own entries from `changelog/elastic/kibana/main/`, without that
 product appearing in the repo's own `docset.yml`. The decision is made per run by
@@ -294,10 +296,13 @@ logic still applies via `assembler.yml`, exactly as for local bundles.
 - **`serve` mode staleness.** The prefetch runs per reload, but within a single `serve` process a
   product's CDN content is pinned until the next reload. Acceptable for now (serve targets local
   markdown authoring, not changelog bundles); revisit alongside the disk cache.
-- **CDN staleness.** The distribution caches the manifest with a 1h default TTL (60s min), so a
-  freshly uploaded bundle may not appear in the CDN-served `registry.json` for up to an
-  hour. If faster propagation is needed the producer (or a docs-actions step) would issue a
-  CloudFront invalidation on registry write.
+- **CDN staleness.** The distribution caches objects with a 1h default TTL (60s min). That applies
+  to `registry.json` manifests **and** to individual entry YAML under
+  `changelog/{org}/{repo}/{branch}/` that `changelog bundle` fetches when sourcing from the CDN.
+  After upload and scrub, the private bucket can hold newer entry content than the CDN serves for
+  up to an hour. Operators who bundle immediately after last-minute entry edits can see stale data until the cache expires. If faster propagation is
+  needed the producer (or a docs-actions step) would issue a CloudFront invalidation on write.
+  User-facing guidance: [Entry sourcing](/cli/changelog/bundle.md#changelog-bundle-entry-sourcing).
 - **Caching key.** When the disk cache lands, use the CDN response ETag (not the registry
   `etag` field) for revalidation.
 
