Well-Architected Framework
Package applications with containers and machine images
Application packaging failures create deployment inconsistencies, security vulnerabilities, and operational overhead. When teams manually configure applications or use inconsistent build processes, each environment drifts from the intended state. Production deployment failures become difficult to reproduce in development. Package your applications into containers and machine images to reduce these risks and achieve consistent, reproducible deployments across all environments.
You can use Packer to automate the creation of both container images and immutable machine images for AWS, Azure, or GCP. With Packer, you define your packaging process as code, ensuring repeatable and reliable builds every time.
Why package applications
Packaging applications addresses the following operational and security challenges:
Eliminate configuration drift: Manual configuration steps introduce inconsistencies between environments. Manual deployments become potential sources of errors when developers must remember and repeat setup steps. Packaging captures your entire application configuration, dependencies, and runtime in a single artifact that deploys identically everywhere.
Reduce deployment time and risk: Teams that build applications at deployment time face longer deployment windows and increased failure rates. Pre-built packages deploy faster and more reliably because the build and test phases happen before deployment, not during it.
Improve security posture: Packaging enables you to scan, test, and approve application artifacts before they reach production. You can enforce security policies at build time, ensuring only verified packages deploy to your infrastructure.
Use Packer for container image builds
Use containers when you need runtime isolation, want to deploy to Kubernetes, Nomad, or other container orchestrators, or manage applications across different operating systems or cloud providers. Containers package your applications with their dependencies into portable images that provide consistent runtime environments.
Build container images from minimal base images and follow security best practices. Include only the necessary dependencies and use multi-stage builds to reduce image size. Tag your images with semantic versions and build hashes for traceability.
Packer automates Docker container image creation with consistent, repeatable builds using templates.
When you use Packer to create your container images, you gain the following benefits:
- Build standardization: Reusable templates reduce build configuration errors and onboarding time for new team members across your organization.
- Multi-registry support: Build identical images across multiple container registries simultaneously, enabling multi-cloud deployments without custom scripts.
- CI/CD integration: Automated image creation catches build failures before they reach production.
- Security enforcement: Apply consistent security scanning and testing to all container images, enforcing your security policies at build time.
- Configuration as code: Version control your image configurations as code, enabling audit trails and rollback capabilities.
The following example shows a Packer template that packages a web application into a Docker container image:
packer {
required_plugins {
docker = {
version = ">= 1.0.8"
source = "github.com/hashicorp/docker"
}
}
}
source "docker" "ubuntu" {
image = "ubuntu:jammy"
commit = true
}
build {
name = "web-app"
sources = [
"source.docker.ubuntu"
]
# Copy application files into the image
provisioner "file" {
source = "dist/"
destination = "/app"
}
# Install runtime dependencies and configure the application
provisioner "shell" {
inline = [
"apt-get update",
"apt-get install -y nodejs npm",
"cd /app && npm install --production",
"useradd -m appuser",
"chown -R appuser:appuser /app"
]
}
# Tag the image for pushing to a registry
post-processor "docker-tag" {
repository = "myregistry.azurecr.io/myapp"
tags = ["1.0.0", "latest"]
}
}
The Packer template packages your application by copying the built application files from the dist/ directory into the container, installing Node.js and production dependencies, creating a non-root user for security, and tagging the result with version numbers. Running packer build produces a complete Docker image containing your application that you can push to a container registry and deploy to Kubernetes or Nomad.
To learn how to build a container with Packer's Docker builder, visit the Packer Docker get started tutorial.
Use Packer for machine image builds
Machine images solve configuration drift and startup time challenges for virtual machine deployments. Use machine images when you need immutable infrastructure, want to minimize startup time by pre-installing dependencies, or deploy to environments where containers are not suitable or available.
For virtual machine deployments, package your application and runtime dependencies directly into the machine image using Packer. This immutable infrastructure approach ensures that your application and its dependencies are baked into the image, eliminating configuration drift and deployment inconsistencies.
Packer automates machine image creation across multiple platforms including AWS, Azure, and GCP using templates.
When you use Packer to create your machine images, you gain the following benefits:
- Multi-cloud support: Create identical machine images for multiple cloud providers from a single template, eliminating platform-specific build scripts.
- Automated provisioning: Use shell scripts, Ansible, Chef, or other configuration tools to reduce manual configuration errors.
- CI/CD integration: Automated image creation catches build failures before they reach production.
- Configuration as code: Version control your image configurations as code, enabling audit trails and rollback capabilities.
The following example shows a Packer template that packages a web application into an AWS AMI:
packer {
required_plugins {
amazon = {
version = ">= 1.2.8"
source = "github.com/hashicorp/amazon"
}
}
}
source "amazon-ebs" "ubuntu" {
ami_name = "myapp-{{timestamp}}"
instance_type = "t2.micro"
region = "us-west-2"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
tags = {
Name = "myapp"
Version = "1.0.0"
Environment = "production"
}
}
build {
name = "web-app"
sources = [
"source.amazon-ebs.ubuntu"
]
# Copy application files to the instance
provisioner "file" {
source = "dist/"
destination = "/tmp/app"
}
# Install dependencies and configure the application
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nodejs npm nginx",
"sudo mv /tmp/app /opt/myapp",
"cd /opt/myapp && sudo npm install --production",
"sudo systemctl enable myapp.service"
]
}
}
The Packer template packages your application into an AMI by copying application files to the instance, installing Node.js and nginx, and configuring the application to start automatically on boot. The template uses timestamp-based naming and tags the AMI with version metadata. Running packer build creates an AMI in your AWS account. Packer outputs the AMI ID (for example, ami-0abcd1234efgh5678), which you can reference in Terraform configurations to launch EC2 instances with your application pre-installed.
To learn how to build a machine image with Packer's AWS builder, visit the Packer AWS get started tutorial.
HashiCorp resources:
- Learn how to deploy packaged applications with Terraform and orchestrators
- Implement automated testing for packaged applications
- Create immutable containers and immutable virtual machines with Packer
- Create semi-automated deployments and fully-automated deployments with Packer
Packer documentation and tutorials:
- Read the Packer documentation for core image building concepts
- Follow hands-on Packer tutorials for image creation
- Learn how to use HCL templates to define Packer builds
- Read about Packer CLI commands including build, init, and validate
- Learn about Packer builders for different platforms
- Use Packer provisioners to configure images with shell scripts, Ansible, or Chef
- Build a Docker image with Packer
- Build an image in AWS, Azure, or GCP
- Build a golden image pipeline with HCP Packer
External resources:
- Read container building best practices from Docker
Next steps
In this section of Automate your workflows, you learned how to package applications for different deployment targets, such as containers and machine images. Packaging is part of the Define and automate processes pillar.
Visit the following documents to learn more about the automation workflow:
- Deploy your packaged applications
- Test your packaged applications before deployment
- Implement a CI/CD system