Getting code from a developer’s laptop to a live AWS environment used to mean a checklist of manual steps: build the artifact, SSH into a server, copy files, restart services, cross your fingers. Jenkins eliminates that manual dance entirely, and when it’s wired up properly with AWS, you get a pipeline that takes a git push and turns it into a live deployment on EC2, ECS, Elastic Beanstalk, S3, or Lambda — automatically, consistently, every single time.
This guide walks through setting up Jenkins for continuous deployment to AWS, covering IAM setup, credentials management, and complete pipeline examples for the most common AWS deployment targets.
Why Jenkins + AWS?
AWS gives you the infrastructure — compute, storage, container orchestration, serverless. Jenkins gives you the automation engine that decides when and how to push code onto that infrastructure. Together they let you implement true continuous deployment: every merge to main can automatically become a production release, with testing and approval gates built into the pipeline itself.
Jenkins Architecture Recap
A quick refresher: your Jenkins controller schedules jobs and stores configuration, while agents actually execute pipeline steps. For AWS deployments, it’s common to run agents as ephemeral EC2 instances or Docker containers that spin up for a build and terminate afterward — this keeps your build environment clean and avoids “works on my agent” drift.
Prerequisites
- A running Jenkins server with admin access
- An AWS account with permissions to create IAM users/roles
- AWS CLI installed on Jenkins agents
- Your application code in a Git repository
Step 1: Create an IAM User for Jenkins
Never use your personal AWS credentials for automation. Create a dedicated IAM user with only the permissions Jenkins actually needs.
- In the AWS Console, go to IAM > Users > Add User
- Name it something like
jenkins-deploy - Select Programmatic access
- Attach a policy scoped to what you’re deploying — for example, for S3 and CodeDeploy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-deploy-bucket",
"arn:aws:s3:::my-deploy-bucket/*"
]
},
{
"Effect": "Allow",
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetDeployment",
"codedeploy:GetApplicationRevision"
],
"Resource": "*"
}
]
}
- Save the access key and secret key — you’ll add these to Jenkins next
For production setups, prefer using an IAM role attached to the EC2 instance running Jenkins (via instance profile) instead of static keys — it’s more secure since there are no long-lived credentials to leak.
Step 2: Install Required Plugins
In Manage Jenkins > Plugins, install:
- AWS Credentials — securely stores AWS access keys
- Pipeline: AWS Steps — adds
withAWSand related steps - Amazon EC2 (optional) — lets Jenkins provision agents dynamically on EC2
- Amazon ECR (optional) — for pushing Docker images to Elastic Container Registry
Step 3: Add AWS Credentials to Jenkins
- Go to Manage Jenkins > Credentials > System > Global credentials
- Click Add Credentials
- Choose AWS Credentials as the type
- Enter the access key ID and secret access key from Step 1
- Give it an ID like
aws-jenkins-creds
Step 4: Example Pipeline — Deploy to S3 + CloudFront (Static Site)
pipeline {
agent any
environment {
AWS_REGION = 'us-east-1'
S3_BUCKET = 'my-static-site-bucket'
CLOUDFRONT_DIST_ID = 'E1EXAMPLE12345'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/yourorg/static-site.git'
}
}
stage('Build') {
steps {
sh 'npm install && npm run build'
}
}
stage('Deploy to S3') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh "aws s3 sync ./dist s3://${S3_BUCKET} --delete"
}
}
}
stage('Invalidate CloudFront Cache') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh "aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_DIST_ID} --paths '/*'"
}
}
}
}
}
Step 5: Example Pipeline — Deploy to ECS (Containerized App)
pipeline {
agent any
environment {
AWS_REGION = 'us-east-1'
ECR_REPO = '123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app'
ECS_CLUSTER = 'my-cluster'
ECS_SERVICE = 'my-service'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/yourorg/my-app.git'
}
}
stage('Build Docker Image') {
steps {
sh "docker build -t ${ECR_REPO}:${BUILD_NUMBER} ."
}
}
stage('Push to ECR') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh """
aws ecr get-login-password --region ${AWS_REGION} | \
docker login --username AWS --password-stdin ${ECR_REPO}
docker push ${ECR_REPO}:${BUILD_NUMBER}
"""
}
}
}
stage('Update ECS Service') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh """
aws ecs update-service \
--cluster ${ECS_CLUSTER} \
--service ${ECS_SERVICE} \
--force-new-deployment
"""
}
}
}
}
post {
success {
echo 'Deployment to ECS succeeded.'
}
failure {
echo 'Deployment failed - rolling back may be required.'
}
}
}
Step 6: Example Pipeline — Deploy to EC2 via CodeDeploy
For applications deployed directly to EC2 instances, AWS CodeDeploy handles the orchestration of stopping the old version, copying new files, and restarting services based on an appspec.yml in your repo.
stage('Deploy via CodeDeploy') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh """
aws deploy create-deployment \
--application-name my-app \
--deployment-group-name production \
--s3-location bucket=${S3_BUCKET},key=app-${BUILD_NUMBER}.zip,bundleType=zip
"""
}
}
}
Using Dynamic EC2 Agents
Rather than running builds on a single static agent, the Amazon EC2 plugin lets Jenkins spin up fresh EC2 instances on demand:
- Go to Manage Jenkins > Clouds > Add a new cloud > Amazon EC2
- Configure your AWS region, credentials, and an AMI to launch from
- Set instance type, security group, and key pair
- Define labels so specific pipelines request these agents (
agent { label 'aws-ec2' })
This is especially useful for teams with spiky build volume — agents launch when needed and terminate when idle, keeping costs down.
Integrating Terraform for Infrastructure
Many teams pair this with Terraform to provision the AWS infrastructure the application deploys to:
stage('Terraform Apply') {
steps {
withAWS(credentials: 'aws-jenkins-creds', region: "${AWS_REGION}") {
sh '''
terraform init
terraform plan -out=tfplan
terraform apply -auto-approve tfplan
'''
}
}
}
Approval Gates for Production Deployments
Continuous deployment doesn’t have to mean zero human oversight. Add a manual approval step before production:
stage('Approve Production Deploy') {
steps {
input message: 'Deploy to production?', ok: 'Deploy'
}
}
Troubleshooting
“Unable to locate credentials” error: The withAWS block isn’t wrapping the AWS CLI call, or the credential ID doesn’t match what’s configured in Jenkins.
ECS service doesn’t pick up the new image: Confirm your task definition uses the :latest tag or that you’re registering a new task definition revision pointing to the new image tag, since force-new-deployment alone won’t pull a new image if the tag hasn’t changed.
Permission denied errors on AWS CLI calls: Double-check the IAM policy attached to the Jenkins user actually includes the specific actions being called — overly narrow policies are a common cause.
EC2 agents fail to connect: Check the security group allows inbound traffic on the Jenkins agent port from the controller, and that the AMI has Java installed.
Security Best Practices
- Use IAM roles with least-privilege policies, scoped per environment (dev/staging/prod)
- Never hardcode AWS keys in Jenkinsfiles — always use the Credentials store
- Enable CloudTrail logging to audit what Jenkins is doing in your AWS account
- Use separate IAM users/roles per environment so a staging deploy can’t touch production
- Rotate access keys regularly, or better, eliminate them entirely using instance profiles or OIDC federation for CI/CD
FAQs
Should I use static AWS access keys or IAM roles for Jenkins? IAM roles (via instance profile if Jenkins runs on EC2, or OIDC federation otherwise) are strongly preferred — they avoid long-lived credentials that could be leaked.
Can Jenkins deploy to AWS Lambda? Yes, using the AWS CLI (aws lambda update-function-code) or the AWS SAM CLI within a pipeline stage, following the same withAWS credential pattern.
What’s the difference between deploying via CodeDeploy versus a plain S3 sync? CodeDeploy orchestrates a controlled rollout (with lifecycle hooks, rollback support) across EC2 instances, while an S3 sync is best suited for static assets or artifacts, not full application deployment logic.
How do I handle secrets like database passwords in the pipeline? Store them in AWS Secrets Manager or Parameter Store and fetch them at runtime with the AWS CLI, rather than putting them in Jenkins credentials or environment variables directly.
Can I trigger a Jenkins pipeline automatically from an AWS event? Yes, using Amazon EventBridge and a webhook, or by having CodeCommit/S3 events invoke a Lambda function that calls the Jenkins remote build trigger API.
Summary
Jenkins and AWS together give you a full continuous deployment pipeline — from a git push all the way to a live update on S3, ECS, EC2, or Lambda. The key building blocks are IAM credentials scoped correctly, the AWS Steps plugin for clean pipeline syntax, and deployment-target-specific stages that fit how your infrastructure is actually organized. Start with a single deployment target, get the credentials and permissions right, then expand to multi-environment pipelines with approval gates as your confidence grows.