An Android App Bundle (AAB) is the publishing format Google Play has required for new apps since August 2021 — and it is the reason two people can install the same app and end up with very different download sizes. You no longer hand Google Play a finished APK. You hand it a bundle that carries all of your compiled code and resources plus the instructions for assembling them, and Google Play generates the split APKs that are actually installed on each device.
That one change moves a lot of decisions out of your build script and into Play: which CPU architecture receives which native libraries, which screen density receives which drawables, which locale receives which strings. It also introduces a set of hard size limits — and the limits moved. The base module ceiling is no longer 200 MB. Teams still planning around the old numbers either over-engineer their delivery strategy or lose an upload to a size error that looks inexplicable.
This guide covers the 2026 picture in the order you meet it: what the bundle contains, the current size limits, how split APKs are assembled, Play Feature Delivery and Play Asset Delivery, the version-code rules for generated APKs, testing with bundletool, and the checklist to run before you upload.
1. What an app bundle actually contains
An app bundle is the .aab artifact you upload instead of an APK. Google's own wording is precise: it includes all of your app's compiled code and resources, and defers APK generation and signing to Google Play. Inside it you will find:
- The base module — the code, resources and manifest every device needs. It carries the app's version code and version name, which is more important than it sounds.
- Feature modules — code and resources that can be delivered at install time, on demand, or conditionally, depending on the device or the country.
- Asset packs — large files (game assets, media, ML models) delivered through Play Asset Delivery rather than through the module system.
- Device-targeting metadata — the manifest settings that let Play decide which pieces belong on which device: ABI, screen density, language, and the newer texture-compression targeting for asset packs.
The bundle itself is never installed on a device. It is a distribution source, not a package.
2. What "split" means for the user
When a user taps Install, Play builds a set of APKs for that specific device configuration, signs them, and serves them. The classic splits are:
- ABI splits — arm64-v8a, armeabi-v7a, x86_64. A modern phone downloads one architecture instead of four.
- Screen density splits — a 420 dpi phone does not pay for 640 dpi raster assets.
- Language splits — strings for locales the device never asked for stay on the server.
- Dynamic feature and asset pack splits — anything you marked for on-demand delivery.
So "download size" in Play Console is not one number, it is a range across configurations, and the App size page reports both the reference-device figure and that range. Judging a release by a single APK you built locally is how teams end up surprised by install conversion.
3. The 2026 size limits
These come from Play Console Help and are the numbers to plan around. Note how far the base module ceiling has moved from the 200 MB figure that still circulates in older guides.
- Base module: 500 MB download size limit.
- Each individual feature module: 500 MB.
- Each individual asset pack: 1.5 GB.
- Cumulative total for all modules plus install-time asset packs: 4 GB.
- Cumulative total for on-demand and fast-follow asset packs: 30 GB.
- Overall maximum compressed download size: 34 GB.
Four further constraints travel with those numbers:
- Apps larger than 1 GB must target minimum SDK Android 5.0 Lollipop (API level 21) or higher.
- The recommended maximum is 100 feature modules when the app targets API 26 or higher; when it targets lower, the ceiling is 50.
- The maximum number of asset packs in one bundle is 100.
- If the app is above 200 MB, users on a mobile data connection see a non-blocking dialog warning about the size when they install it. It does not block the install; it does cost conversions.
One legacy trap: apps still delivered as plain APKs are not covered by any of the limits above. They remain on the legacy APK rules, where the ceiling is a 100 MB APK. If an upload rejects with a size error that looks like a message from 2019, check whether the artifact you uploaded is genuinely a bundle.
4. Which mechanism do you need?
Three mechanisms solve three different problems, and mixing them up costs weeks.
- Play Feature Delivery — modules of code and resources. Use it when you want functionality that not every device or market should install.
- Play Asset Delivery — large binary assets. Use it for game content, video, audio, ML models and large datasets where the install footprint matters.
- Play App Signing — adjacent but central: with bundles, Play signs the generated APKs, which is why key and version-code management changed.
5. Play Feature Delivery: install-time, on-demand, conditional
A feature module declares one of three delivery modes in its manifest:
- install-time — the module is part of the initial install. Users cannot skip it, and it counts toward the size the user sees.
- on-demand — the module is not installed until the app requests it. Your code must handle the pending, downloading, completed and failed states.
- conditional — the module is installed at install time only where the device meets a condition you declare, such as device features, user country, or minimum SDK version.
Conditional delivery is the one teams underuse. A tablet-only feature module, or one that only makes sense where a particular payment provider operates, does not need to sit on every device.
6. Play Asset Delivery: install-time, fast-follow, on-demand
- install-time packs arrive with the app. They count toward the app's install size — and toward the 4 GB cumulative limit. Convenient, but still part of the number users see in the store listing.
- fast-follow packs start downloading automatically as soon as the install finishes, in the background. The user can open the app immediately; the assets land shortly after.
- on-demand packs download only when the app asks for them. This is where the large numbers live: on-demand and fast-follow packs have a 30 GB cumulative allowance precisely because they are not competing with install conversion.
Asset packs also support texture-compression targeting, which lets Play deliver the compression format a device's GPU actually supports instead of bundling every format. For games this is usually the single largest size win available.
7. Version codes when Play generates the APKs
With bundles you stop managing version codes per APK. You set one version code in the base module, and after upload Google Play assigns that same version code to every APK it generates from that bundle. All split APKs for one install share the version code.
The consequence is discipline, not complexity: to ship an update you increment the base module's version code and build a new, full bundle. When the user updates, Play serves updated versions of every APK currently installed on that device. Two practical rules follow:
- Do not keep the old per-ABI version-code scheme (the classic
abiCodes * 1000 + versionCodetrick). It is a legacy APK-splitting pattern and it collides with bundle uploads. - If a build system mangles version codes across tracks, uploads fail with "version code already used" or "lower than the existing version". The base module is the single source of truth.
8. Signing: the upload key, not the app signing key
With Play App Signing, the artifact you upload is signed with an upload key, and Play re-signs the generated APKs with the app signing key. The signing configuration belongs in the base module only. Two things routinely catch people out:
- If the upload key is lost, it can be reset through Play Console — it is recoverable. The app signing key is not something you need to handle at all.
- If a bundle is signed by the wrong key, Play rejects the upload before review even starts. That is a build-configuration problem with a one-line fix, not a policy problem.
9. Testing split APKs before release with bundletool
The Android Gradle plugin builds the bundle; bundletool is what you use to turn it back into device-specific APKs and to emulate Play's distribution locally. Play itself uses the same tool to generate APKs, so local behaviour tracks production closely.
# Build an APK set covering every supported device configuration
bundletool build-apks \
--bundle=app-release.aab \
--output=app.apks \
--ks=upload-key.jks --ks-key-alias=upload
# Deploy only the splits that match the connected device
bundletool install-apks --apks=app.apks
# Produce a universal APK for a quick sanity check
bundletool build-apks --bundle=app-release.aab --output=app.apks --mode=universal
# Enable local testing of on-demand modules and asset packs
bundletool build-apks --bundle=app-release.aab --output=app.apks --local-testing
The --local-testing flag writes metadata into the APK manifests that tells the Play Core libraries to use the local splits instead of contacting Play. Without it, on-demand download code cannot be exercised outside the store. For a device-specific spec, use bundletool get-device-spec.
Before a production rollout, the internal testing track is still the cheapest way to confirm that real installs resolve the right splits — including on the low-end devices your crash analytics care about.
10. Read the App size page before you optimise anything
Once an app has a production release, Play Console reports download size under Monitor and improve, then Android vitals, then App size. It shows the app's download size on a reference device and the range across configurations, a peer comparison for a group you define, the trend over time, and two metrics worth more than the size number itself:
- Active devices with less than 2 GB free — how much of your install base is under storage pressure.
- Uninstalls on devices with less than 2 GB free — the ratio of uninstalls on those devices to uninstalls overall. A high value is a size problem wearing a retention costume.
11. Pre-upload checklist
- The artifact is an
.aab, not a universal APK. - The base module version code is incremented in exactly one place.
- The upload key is configured in the base module only, and the keystore is backed up somewhere that is not one laptop.
- The base module compressed download size is within 500 MB — and under 200 MB if you want to avoid the mobile-data warning on install.
- Each feature module is under 500 MB, and all modules plus install-time packs are under the 4 GB cumulative limit.
- Each asset pack is under 1.5 GB, and on-demand plus fast-follow packs stay under 30 GB.
- No more than 100 asset packs, and within the 100 or 50 feature-module guidance for your minimum SDK.
- Minimum SDK is API 21 or higher if the app exceeds 1 GB.
- On-demand features are implemented through the Play Core libraries, with the pending, downloading and failed states handled explicitly.
- Texture-compression targeting is enabled for asset packs where more than one GPU format is in play.
- The bundle has been tested with
bundletool install-apkson at least one low-end and one arm64 device, plus--local-testingfor every on-demand path. - The App size page is reviewed after the release goes live, including the less-than-2-GB-free metrics.
- The data safety form and content declarations still match what the new modules actually collect.
- The store listing text and screenshots still describe the app the user now downloads, not the universal build you tested with.
12. Failures that only appear after release
- An on-demand module never downloads because the Play Core integration assumes success and has no retry path for a failed or cancelled request.
- An install-time asset pack balloons the listing size after a content drop, and install conversion falls with no code change at all.
- Conditional modules install on devices that do not meet the condition because it was declared in the wrong manifest.
- Version-code collisions when CI stamps the bundle while a legacy per-ABI scheme is still active in the build files.
- A wrong-key signature after a keystore rotation that was never mirrored into the base module configuration.
- First-run failures on slow connections when the app's first screen depends on a fast-follow pack that has not arrived yet.
- A data safety mismatch after a new SDK lands inside a newly split module that nobody re-declared.
13. Where KappS fits in
App size and delivery modes sit in the same operational chain as the developer account, the payments profile and the tax information: any one of them can quietly hold up a release. KappS helps developers prepare and submit compliant material across that chain — including bundle and delivery configuration reviews, where the difference between an install-time pack and a fast-follow pack is measured in install conversion, not in megabytes.
What we do not do is work around the store. Size limits, delivery declarations and data safety answers are checked by Google against the artifact you actually shipped; a listing that promises something the bundle does not do is a fast route to a rejected release or a suspended account, and it is not a service we offer. The whole trick is the one described above: build the bundle honestly, declare what it really contains, and respect the limits.
Sources (Google official documentation): About Android App Bundles (developer.android.com/guide/app-bundle); Play Console Help — Optimise your app's size and stay within Google Play app size limits (support.google.com/googleplay/android-developer/answer/9859372); Configure the base module; Play Asset Delivery; Play Feature Delivery; bundletool (developer.android.com/tools/bundletool).