SaveMyCert
Log in
5 of 5 free questions left today·for unlimited practice
Deployment

Preparing Application Artifacts for AWS Deployment

14 min readDVA-C02 · DeploymentUpdated

Preparing application artifacts means packaging your code, its dependencies, and its runtime expectations into the exact format each AWS compute service accepts — a zip archive or container image for Lambda, container images in Amazon ECR for ECS and EKS, and a source bundle for Elastic Beanstalk. Deployment is 24% of the DVA-C02 exam, and Task 3.1 is its foundation: before pipelines or deployment strategies matter, you must know precisely what you are shipping. The exam probes whether you can bundle dependencies correctly, lay out a SAM project so builds resolve handler paths, decide what belongs in Git versus an artifact store, size memory and CPU for Lambda and ECS, and apply the principle behind many questions — build one artifact, then inject environment-specific configuration at deploy time with AWS AppConfig, Parameter Store, or environment variables. This lesson works through each packaging format, the stores that hold artifacts, and the config-outside-the-artifact rule that makes promotion between environments safe.

What you’ll learn
  • Match each AWS compute target to its deployable artifact format: Lambda zip or container image, ECS/EKS images in ECR, Elastic Beanstalk source bundles
  • Package dependencies correctly using bundled libraries, lock files, Lambda layers, and container image layers
  • Organize a SAM/CloudFormation project so handler paths, build output, and template references resolve at deploy time
  • Decide what lives in the Git repository versus S3, Amazon ECR, and AWS CodeArtifact
  • Apply resource requirements — Lambda's coupled memory/CPU model versus ECS task-level and container-level settings
  • Prepare environment-specific configuration with environment variables, Parameter Store, and AWS AppConfig so one artifact serves every environment

What counts as a deployable artifact on AWS

A deployable artifact is the packaged unit a compute service actually runs — not your repository, but the built output of it. Each AWS compute target defines its own format, and the exam expects you to match artifact to service instantly.

AWS Lambda accepts two packaging formats. A .zip archive holds your code plus bundled dependencies: up to 50 MB zipped for direct upload and 250 MB unzipped including all layers. A container image pushed to Amazon ECR can be up to 10 GB and lets you bring OS-level packages and heavyweight libraries. Amazon ECS and Amazon EKS run container images pulled from a registry — on this exam, that registry is Amazon ECR. AWS Elastic Beanstalk deploys a source bundle: a zip (or war for Java) with your application files at the root of the archive, not nested inside a parent folder — a bundle zipped one directory too high deploys but fails to start, a classic gotcha.

Lambda .zipLambda container image
Size limit50 MB zipped (direct upload), 250 MB unzipped including layersUp to 10 GB
Stored inDirect upload or Amazon S3Amazon ECR
DependenciesBundled in the archive, plus up to 5 layersImage layers you fully control
RuntimeAWS managed runtimesBase image you choose, including custom runtimes
Best forTypical functions, fastest path to deployLarge dependencies, custom runtimes, existing container tooling

When a scenario mentions a dependency measured in gigabytes, a custom runtime, or a team standardized on Docker, choose the container image. When it stresses simplicity and managed runtimes, the zip wins.

Managing dependencies inside the package

Dependencies must travel inside the artifact, because the compute service installs nothing for you at run time. A Lambda zip that imports a library not present in the archive or a layer fails with an import error on first invoke — the exam loves this failure mode.

For zip packaging, you bundle third-party libraries alongside your code: pip install -r requirements.txt -t . for Python, or shipping node_modules for Node.js. sam build automates this — it reads each function's manifest (requirements.txt, package.json) and produces a complete, deployable folder per function.

Lock files make those builds reproducible. package-lock.json, pinned requirements.txt entries, or poetry.lock guarantee that the dependency tree resolved in CI today matches the one resolved next month, so the artifact you tested is the artifact you ship. Commit lock files to the repository.

Lambda layers extract shared dependencies into a separately versioned archive that multiple functions attach. A function can use up to 5 layers, and layer contents still count toward the 250 MB unzipped limit. Layers shine when ten functions share the same SDK wrapper or a large library: you update the layer version once instead of rebuilding ten zips. They are not a way around the size limit.

Container image layers serve the same goal through Docker's cache: order your Dockerfile so the base image and dependency-install steps come before the COPY of application code. Code changes then rebuild only the final, small layer, keeping builds and pushes fast.

Organizing files and directory structure for deployment

Directory structure is not cosmetic — the deployment tooling resolves paths from it, and a wrong path is a broken deploy. A conventional SAM project keeps template.yaml at the repository root, one folder per function (each with its own code and dependency manifest), an events/ folder of sample payloads, and a tests/ folder.

Two template properties tie the structure together. CodeUri points at the function's source directory, and Handler names the entry point within it. For Python, Handler: app.lambda_handler means the file app.py at the root of CodeUri must define a function called lambda_handler. If a question shows a handler error — "Unable to import module 'app'" — check whether the file name, the function name, or the directory in CodeUri disagrees with the Handler string.

Build output is separate from source. sam build writes resolved artifacts and a rewritten template to .aws-sam/build/, and subsequent sam deploy ships from there. That directory is generated, so it belongs in .gitignore, along with node_modules and any local .env files.

Elastic Beanstalk has its own structural rule worth repeating: the source bundle's files sit at the archive root. Zip the contents of your project folder, not the folder itself. Optional configuration files for the environment live in an .ebextensions directory inside the bundle.

The pattern across all targets: source layout serves the build, and the build produces the artifact — never deploy the raw working tree.

Code repositories versus artifact stores

The repository and the artifact store hold different things, and mixing them up is both an exam distractor and a real-world incident. The Git repository holds source code, the IaC templates (template.yaml, CDK code), dependency manifests and lock files, and configuration templates — the names of settings, not their production values. It never holds secrets, built artifacts, or generated directories.

Artifact stores hold built outputs, versioned and immutable: Amazon S3 for Lambda zips and packaged CloudFormation templates (sam deploy uploads there automatically), Amazon ECR for container images, and AWS CodeArtifact for package dependencies. CodeArtifact is the one candidates forget: it is a managed repository for npm, PyPI, Maven, and NuGet packages. It can proxy public registries through an upstream connection, cache what your builds pull, and let an organization publish private internal packages — so builds keep working if a public package is removed, and teams consume only approved versions. When a question describes "a private npm registry" or "controlling which package versions builds can use", the answer is CodeArtifact, not S3.

The workflow that connects them: developers commit to a feature branch, merge to main through review, and the build system produces the artifact from the repository and publishes it to the artifact store. Deployments then reference the store — an S3 key, an image URI, a package version — never a developer's laptop. The repository is where code is authored; the store is where deployable, versioned outputs live.

Applying resource requirements: memory and CPU per target

Resource requirements are part of preparing the artifact for its environment, and each compute service models them differently.

Lambda has a single knob: memory. You allocate between 128 MB and 10,240 MB, and CPU scales proportionally with it — at 1,769 MB a function receives the equivalent of one vCPU. There is no separate CPU setting. This coupling is heavily tested: if a function is CPU-bound, the fix is to increase memory, even if memory usage itself is low. Because Lambda bills on GB-seconds, more memory can also reduce cost when the added CPU shortens duration enough — right-sizing means measuring duration at several memory settings, not guessing.

ECS separates task-level and container-level settings. The task definition can declare task-level cpu and memory — the total envelope for all containers in the task, and required when running on Fargate. Each container can then declare its own limits: cpu shares, memory as a hard limit (the container is killed if it exceeds it), and memoryReservation as a soft guarantee. A multi-container task — say an app container plus a logging sidecar — uses container-level settings to divide the task's envelope.

Right-sizing is a measurement loop. Start from observed usage, set requirements slightly above peak, and revisit after load changes. On the exam, answers that hardcode generous allocations "to be safe" lose to answers that measure and adjust — over-provisioning wastes money, under-provisioning causes throttling, OOM kills, or timeouts.

Environment-specific configuration: same artifact, different config

The core principle of Skill 3.1.5 is that configuration lives outside the artifact. You build one artifact, verify it in dev and staging, and promote that same artifact to production — only the injected configuration changes per environment. Baking environment values into the package means production runs a different, never-tested build, and rebuilding per environment is the wrong answer on the exam almost every time it appears.

The injection mechanisms, in increasing sophistication:

  • Environment variables — set per environment on the Lambda function, ECS task definition, or Elastic Beanstalk environment. Simple and universal, but changing one requires a redeploy or configuration update, and plaintext values are unsuitable for secrets.
  • SSM Parameter Store — centralized parameters, organized hierarchically (for example /myapp/dev/db-endpoint versus /myapp/prod/db-endpoint), with SecureString parameters encrypted via AWS KMS. The application reads them at startup or on a refresh interval; CloudFormation and SAM templates can also resolve them at deploy time.
  • AWS AppConfig — purpose-built for runtime application configuration and feature flags. You model an application, its environments, and configuration profiles, then deploy configuration the way you deploy code: a validator (a JSON Schema or a Lambda function) checks the data first, a deployment strategy rolls it out gradually over a defined time with a bake period, and CloudWatch alarms trigger automatic rollback. That lets you flip a feature flag or tune a setting in production with no code deploy and a controlled blast radius.

Trigger words: "without redeploying", "feature flag", "validate configuration before rollout", "gradually roll out a setting" — all point at AppConfig.

Container images for deployment: ECR, tags, and immutability

For container-based targets — ECS, EKS, and container-image Lambda — the artifact is an image in Amazon ECR, and preparing it means building, tagging, and pushing. The push flow to know: authenticate Docker to the registry with aws ecr get-login-password piped into docker login, then docker build, docker tag the image with the full ECR repository URI, and docker push. CI systems script exactly this sequence.

Tags are how deployments reference images, and by default tags are mutable — pushing a new image with an existing tag silently repoints that tag. This is why deploying latest is an anti-pattern: an ECS service defined against myapp:latest can pull different code on each task launch, so the "same" service runs mixed versions and rollback becomes guesswork. Deployments should reference a unique, meaningful tag per build — a version number or the Git commit SHA.

Tag immutability is an ECR repository setting that enforces this: once v1.4.2 is pushed, any attempt to push a different image under that tag is rejected. That makes a tag a permanent, auditable pointer, which is exactly what a promotion pipeline needs — the image tag tested in staging is provably the image running in production. For the strongest guarantee, a deployment can reference the image digest (the content hash), which identifies exactly one image forever regardless of tags.

ECR also supports scan-on-push vulnerability scanning and lifecycle policies that expire old untagged images — housekeeping details that keep the registry from growing unbounded.

Scenario: one artifact promoted through three environments

Walk through how these pieces combine. Your team owns a Python API: an API Gateway fronting Lambda functions, defined in a SAM template, deployed to dev, staging, and prod accounts-worth of stacks.

Build once. CI checks out the repository, runs sam build — which resolves each function's pinned requirements.txt into .aws-sam/build/ — and packages the artifacts to S3. The output of this stage, the built template plus the uploaded zips, is the single artifact for this release. Nothing downstream rebuilds it.

Parameterize the template. The template declares a Stage parameter and uses it where resources differ: the function's environment variables include STAGE and a Parameter Store path prefix like /orders-api/prod/. The Lambda memory size is itself a parameter — dev runs at a small size, prod at the measured optimum.

Promote with overrides. The pipeline deploys the same packaged template three times: sam deploy --parameter-overrides Stage=dev, then staging, then prod, each into its existing stack. Because only parameters change, the code verified in staging is byte-for-byte what production runs.

Inject runtime config. Database endpoints and per-environment settings live in Parameter Store under each environment's prefix; the new feature this release introduces is behind an AppConfig feature flag, deployed separately with a gradual rollout strategy and an alarm-based rollback — so enabling it in prod is a configuration deployment, not a code deployment.

Every Task 3.1 skill appears here: packaged dependencies, a resolvable structure, artifacts in S3, sized resources, and configuration injected per environment.

Tip. Expect scenario questions that hand you a compute target and ask what to ship: a dependency "larger than the Lambda package limit" (container image), "shared libraries across many functions" (a layer), or a Beanstalk bundle that fails because files are nested in a folder. Configuration questions hinge on trigger phrases — "without redeploying the application", "feature flags", "validate before rollout", "gradually release a configuration change" all point to AWS AppConfig, while "encrypted parameter" points to Parameter Store SecureStrings. Sizing questions test the Lambda memory-CPU coupling: a CPU-bound function is fixed by raising memory. Distractors often propose rebuilding the artifact per environment or deploying the latest image tag — both are wrong.

Key takeaways
  • Lambda zip packages: 50 MB zipped for direct upload, 250 MB unzipped including layers; Lambda container images go up to 10 GB via ECR.
  • Match artifact to target: zip or container image for Lambda, ECR images for ECS/EKS, and a root-level zip source bundle for Elastic Beanstalk.
  • Lambda layers share dependencies across functions (up to 5 per function) but still count toward the 250 MB unzipped limit — they are not a size workaround.
  • Lambda has one sizing knob: CPU scales with memory (about 1 vCPU at 1,769 MB); ECS separates task-level envelopes from container-level limits.
  • Build one artifact and promote it unchanged; inject per-environment values via environment variables, Parameter Store, or AWS AppConfig — never rebuild per environment.
  • AppConfig deploys configuration like code: validators (JSON Schema or Lambda) check it, deployment strategies roll it out gradually, and alarms trigger rollback.
  • Source, IaC templates, and lock files live in Git; built artifacts live in S3/ECR, package dependencies in CodeArtifact; secrets live in none of these.
  • Enable ECR tag immutability (or pin digests) so a deployed tag can never silently point at different code.

Frequently asked questions

What is the maximum size of an AWS Lambda deployment package?

A Lambda zip deployment package can be up to 50 MB when uploaded directly and up to 250 MB unzipped, including all attached layers. If your code and dependencies exceed that, package the function as a container image instead — images stored in Amazon ECR can be up to 10 GB.

Should I use a Lambda layer or bundle dependencies in the zip?

Bundle dependencies in the zip when one function uses them — it is the simplest, most self-contained option. Use a layer when several functions share the same dependencies, so you version and update the shared code once instead of rebuilding every function. Remember that a function supports at most 5 layers and layer contents still count toward the 250 MB unzipped limit.

What is the difference between AWS AppConfig and Parameter Store?

Parameter Store is a key-value store: applications read parameters (including KMS-encrypted SecureStrings) organized in hierarchies like /myapp/prod/. AWS AppConfig is a configuration deployment service built on top of the same idea: it validates configuration data before release, rolls it out gradually using deployment strategies with a bake time, and rolls back automatically on CloudWatch alarms — which makes it the right choice for feature flags and any setting you change at runtime in production.

Why should the same artifact be deployed to every environment?

Because testing only proves things about the exact build you tested. If you rebuild per environment, production runs a build that never went through staging — different dependency resolution, different bytes. Building once and injecting environment-specific configuration at deploy time (environment variables, Parameter Store, AppConfig) guarantees the artifact verified in staging is identical to the one in production.

What belongs in the Git repository versus the artifact store?

The repository holds what humans author: source code, SAM/CloudFormation templates, dependency manifests, and lock files. Artifact stores hold what builds produce: zips and packaged templates in S3, container images in ECR, and shared package dependencies in CodeArtifact. Secrets belong in neither — keep them in Secrets Manager or SecureString parameters.

What does ECR tag immutability do?

With tag immutability enabled on an ECR repository, a tag can be pushed exactly once — any later push reusing that tag is rejected. This prevents a tag like v1.4.2 from being silently repointed at different code, so the image you tested under a tag is provably the image production pulls. For an even stronger reference, deploy by image digest.

Test yourself on this topic
Practice questions with full explanations.
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.