Deploy a Website with GitHub Actions
Build a CI/CD pipeline that auto-deploys your site to AWS S3 on every push.
Introduction
30 Second Summary
Every time you update a website by manually uploading files, you risk forgetting a step or pushing the wrong version live. Professional teams avoid this by automating the entire process.
In this project, you will build a live website on AWS S3 with a GitHub Actions pipeline that deploys it automatically. Every time you push a change to your code, your site updates within seconds.
What You'll Build
A simple HTML website hosted on Amazon S3, with a fully automated CI/CD deployment pipeline using GitHub Actions.
By the end of this project, you'll have:
- A public S3 bucket configured for static website hosting, accessible via a public URL.
- An IAM user with least-privilege permissions connecting GitHub to AWS.
- A GitHub Actions workflow that automatically deploys your code on every push.
- Secret Mission: Upgrade to OIDC authentication, eliminating stored credentials entirely.
Are there any prerequisites?
This project uses an AWS account and a GitHub account. Both are free to create if you don't have them yet.
Before We Start
Before We Start...
Install AWS CLI and Git
Before building your deployment pipeline, you need two tools on your machine: Git and the AWS CLI.
Git tracks your code changes and pushes them to GitHub. The AWS CLI lets you manage AWS resources directly from your terminal.
In this step, get ready to:
- Install and configure Git.
- Install and configure the AWS CLI.
- Verify your CLI is authenticated to your AWS account.
Install Git
Git is a version control system that tracks every change you make to your code. You'll use it to push your website files to GitHub, which triggers the automated deployment.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter to open it.
- Open the integrated terminal by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Check if Git is already installed by running this command:
git --version
✔️ I see a version number
Git is ready on your machine. Skip ahead to configuring your Git identity below.
ⓧ Command not found
- Install Git by running this command:
winget install --id Git.Git -e --source winget
- After the installation finishes, close your terminal by pressing Ctrl+` (Windows) or Cmd+` (macOS), then press it again to reopen a fresh terminal.
- Confirm Git is installed by running git --version again. You should see a version number in the output.
Command not recognized?
If winget is not recognized, download the Git installer directly from https://git-scm.com/install/windows. Run the installer and accept all defaults.
If git --version still fails after installing, close VS Code completely and reopen it. The terminal needs to reload your system PATH.
help me install Git on Windows
Whether Git was already installed or you just installed it, you need to configure your identity. Git attaches your name and email to every commit you make.
- Set your Git username by running this command:
git config --global user.name "[[YOUR_GIT_NAME="Your Name"]]"
- Set your Git email by running this command:
git config --global user.email "[[YOUR_GIT_EMAIL="your-email@example.com"]]"
Why configure a username and email?
Git stamps your name and email on every commit. This identifies you as the author when your code is pushed to GitHub.
Install the AWS CLI
The AWS CLI is a command-line tool that lets you create, configure, and manage AWS services without leaving your terminal. You'll use it to deploy files to S3.
- Check if the AWS CLI is already installed by running this command:
aws --version
✔️ I see a version number
The AWS CLI is ready. Skip ahead to configuring your credentials below.
ⓧ Command not found
- Download the AWS CLI installer from this URL:
https://awscli.amazonaws.com/AWSCLIV2.msi
- Open the downloaded AWSCLIV2.msi file and follow the installation prompts. Accept all defaults.
- After installation finishes, close your VS Code terminal and reopen it by pressing Ctrl+` (Windows) or Cmd+` (macOS) twice.
- Confirm the AWS CLI is installed by running aws --version again. You should see a version number in the output.
Still seeing command not found?
The installer requires admin rights on Windows. Right-click the .msi file and select Run as administrator if the standard install did not work.
If the command still fails, close VS Code completely and reopen it so the terminal picks up the updated system PATH.
help me install the AWS CLI on my machine
Now you need to connect the AWS CLI to your AWS account. This requires an access key, which acts like a password for your terminal to talk to AWS.
- Open the AWS Console in your browser at https://console.aws.amazon.com and sign in.
- In the navigation bar on the upper right, click your username.
- Select Security credentials.
- Scroll down to the Access keys section.
- Click Create access key.
- On the use case page, select Command Line Interface (CLI).
- Check the acknowledgment box at the bottom and click Next.
- Click Create access key.
- Copy your Access key ID and Secret access key to a safe location. Keep this page open.
This is your only chance to copy the secret key
AWS shows the secret access key only once. If you close this page without copying it, you will need to delete the key and create a new one.
- Back in your VS Code terminal, configure the AWS CLI by running the command below. It will ask you four questions interactively:
aws configure
- When prompted for AWS Access Key ID, paste your access key ID and press Enter.
- When prompted for AWS Secret Access Key, paste your secret access key and press Enter.
- When prompted for Default region name, enter us-east-1 and press Enter.
- When prompted for Default output format, enter json and press Enter.
Where does aws configure store your credentials?
The aws configure command saves your credentials in a file at %UserProfile%\.aws\ on Windows (or ~/.aws/ on macOS). The CLI reads this file automatically every time you run an aws command.
Verify both tools work together
With both tools installed and configured, you can now confirm that your terminal can actually authenticate to your AWS account.
- Confirm your CLI is authenticated by running this command:
aws sts get-caller-identity
What should I see?
You should see a JSON response containing three fields: your AWS account ID, your user ARN, and your user ID. This confirms your terminal can communicate with AWS.
If you see an error instead, double-check that you pasted the correct access key ID and secret access key during aws configure.
Seeing an InvalidClientTokenId error?
This means your access key ID or secret access key was entered incorrectly. Run aws configure again and carefully re-paste both values.
Make sure there are no extra spaces before or after the key values when you paste them.
help me fix my AWS CLI authentication
Your tools are installed and authenticated. Next up, you'll create an S3 bucket and configure it to host your website.
Create and Configure an S3 Bucket
Your tools are installed and your CLI is authenticated to AWS. Now it's time to create the actual infrastructure that will host your website.
Amazon S3 is a cloud storage service that can also serve files directly as a website. In this step, you'll create an S3 bucket, turn on static website hosting, and open the bucket to the public so anyone can view your site.
In this step, get ready to:
- Create an S3 bucket in the AWS Console.
- Enable static website hosting on the bucket.
- Configure a bucket policy for public access.
Create an S3 bucket
An S3 bucket is a container that holds your files in the cloud. You need one to store the HTML that makes up your website.
- Sign in to the AWS Management Console at https://console.aws.amazon.com/s3/.
- In the left navigation pane, click General purpose buckets.
- Click Create bucket.
- In the Bucket name field, enter a unique name like my-devops-site-yourname (replace yourname with your actual name).
- For AWS Region, select US East (N. Virginia) us-east-1.
- Leave all other settings at their defaults.
- Scroll down and click Create bucket.
Why does my bucket name need to be unique?
S3 bucket names are globally unique across all AWS accounts. Your bucket name becomes part of the public URL for your website, so no two people in the world can have the same bucket name.
Write down your bucket name
You'll use your bucket name multiple times throughout this project. Keep it handy.
Seeing a 'bucket already exists' error?
Someone else already owns that name. Try adding numbers or your initials to make it unique. Bucket names can only contain lowercase letters, numbers, and hyphens.
help me fix my S3 bucket naming error
Enable static website hosting
By default, S3 buckets just store files. To serve them as a website, you need to turn on the static website hosting feature.
- Click on your newly created bucket name in the bucket list.
- Click the Properties tab.
- Scroll down to the Static website hosting section.
- Click Edit.
- Select Enable.
- In the Index document field, enter index.html.
- Click Save changes.
After saving, you'll see the Static website hosting section now shows a Bucket website endpoint URL. Copy this URL and save it somewhere. It looks like this:
http://[[BUCKET_NAME="my-devops-site-yourname"]].s3-website-us-east-1.amazonaws.com
What is this endpoint URL?
This is the public web address where your site will be accessible. Anyone with this URL can view your website once you finish the public access configuration in the next substep.
Configure public access
S3 buckets are private by default. For your website to work, you need to allow the public to read your files. This requires two changes: disabling the Block Public Access setting and adding a bucket policy.
- Click the Permissions tab.
- In the Block public access (bucket settings) section, click Edit.
- Uncheck Block all public access.
- Click Save changes.
- A confirmation dialog appears. Type confirm in the text field and click Confirm.
Why disable Block Public Access?
AWS enables Block Public Access by default to protect your data. Since you're building a public website, you need to disable this protection so visitors can download and view your HTML files.
Now you need to add a bucket policy that explicitly grants read access to everyone.
- Still on the Permissions tab, scroll down to the Bucket policy section.
- Click Edit.
- Paste the following JSON policy into the policy editor, replacing your-bucket-name with your actual bucket name:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[[BUCKET_NAME="my-devops-site-yourname"]]/*"
}
]
}
What does this policy do?
This bucket policy is a JSON document that defines who can access your bucket and what they can do.
- "Principal": "*" means anyone in the world.
- "Action": "s3:GetObject" means they can only read (download) files.
- "Resource" specifies which bucket and objects the policy applies to. The /* at the end means all objects inside the bucket.
- Click Save changes.
You should see a red Publicly accessible badge appear on your bucket's Permissions tab. This confirms your bucket is now open for public website hosting.
Getting an 'Access Denied' error when saving the policy?
Make sure you disabled Block Public Access first. The bucket policy will fail to save if Block Public Access is still enabled.
Also check that you replaced your-bucket-name in the Resource line with your actual bucket name. The ARN must match exactly.
help me fix the Access Denied error on my S3 bucket policy
Your S3 bucket is fully configured for website hosting. Next up, you'll create a real HTML page, push it to GitHub, and upload it to this bucket to see your site go live.
Create Your Website and Push to GitHub
Your S3 bucket is configured and ready to serve web pages to anyone on the internet. Now you need something to serve.
In this step, you'll create a simple HTML webpage, track it with Git, push it to GitHub, and manually upload it to S3 to confirm everything works end to end.
In this step, get ready to:
- Create an HTML file for your website.
- Push your code to a GitHub repository.
- Upload to S3 and verify your live site.
Create a simple HTML file
Every website starts with an HTML file. You'll create a project folder and build a simple webpage that your S3 bucket will serve to visitors.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- Open VS Code's integrated terminal by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir my-devops-site
cd my-devops-site
- Confirm you're inside the new folder by running:
pwd
You should see a path ending in my-devops-site.
- Open this folder in VS Code by clicking File in the top menu bar.
- Click Open Folder.
- Select the my-devops-site folder on your Desktop, then click Open.
VS Code now has your project folder loaded. You'll see it in the Explorer sidebar on the left.
- Create a new file by right-clicking in the Explorer sidebar and selecting New File.
- Name the file index.html and press Enter.
- Paste the following HTML into index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My DevOps Site</title>
</head>
<body>
<h1>Hello from AWS S3!</h1>
<p>This site is deployed automatically using GitHub Actions.</p>
<p>Every time I push a change, it goes live within seconds.</p>
</body>
</html>
What does this code do?
This is a standard HTML webpage with three visible elements.
- The <h1> tag creates the main heading visitors see.
- The two <p> tags display paragraph text below the heading.
- The <title> tag sets the text shown in the browser tab.
- Save index.html by pressing Ctrl+S (Windows) or Cmd+S (macOS).
Don't see the file in your Explorer sidebar?
Make sure you opened the my-devops-site folder itself, not its parent (Desktop). The folder name should appear at the top of the Explorer panel.
If you accidentally created the file outside the folder, delete it and create it again inside the correct folder.
Help me find my index.html file in VS Code
✔️ Awesome, I've got everything!
Great. Make sure you've saved index.html before moving on.
ⓧ I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My DevOps Site</title>
</head>
<body>
<h1>Hello from AWS S3!</h1>
<p>This site is deployed automatically using GitHub Actions.</p>
<p>Every time I push a change, it goes live within seconds.</p>
</body>
</html>
Initialize a Git repository and push to GitHub
Git tracks every change you make to your files. GitHub stores your code online so that other tools (like a CI/CD pipeline) can access it.
- Open the integrated terminal in VS Code by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Initialize a Git repository and make your first commit by running these commands:
git init
git add .
git commit -m "Initial commit"
git branch -M main
What do these commands do?
- git init turns your folder into a Git repository that tracks file changes.
- git add . stages all files in the folder for your first commit.
- git commit -m saves a snapshot of your staged files with a message.
- git branch -M main renames the default branch to main, which is what your CI/CD pipeline will listen for later.
Now you need a GitHub repository to push this code to.
- Open your browser and go to github.com.
- Click the + icon in the top-right corner of the page.
- Select New repository from the dropdown.
- Enter my-devops-site in the Repository name field.
- Leave all other settings at their defaults. Do not check Add a README file.
- Click Create repository.
Why skip the README?
You already have files in your local repository. If GitHub creates a README, your local and remote repos will have conflicting histories, making the first push more complicated.
GitHub now shows a page with setup instructions. You'll use the commands for pushing an existing repository.
- Connect your local repository to GitHub and push your code by running these commands:
git remote add origin [[REPO_URL="https://github.com/YOUR-USERNAME/my-devops-site.git"]]
git push -u origin main
What do these commands do?
- git remote add origin tells Git where your GitHub repository lives.
- git push -u origin main uploads your committed code to GitHub's main branch.
- Refresh your GitHub repository page in the browser.
You should see index.html listed in your repository's file list.
Push failed or asked for credentials?
If Git asks for a username and password, GitHub no longer accepts passwords for Git operations. You need a Personal Access Token (PAT) instead. Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic) to generate one.
If you see "failed to push some refs", make sure you did not initialize the GitHub repo with a README.
Help me push my code to GitHub
Upload manually to verify S3 works
Before automating anything, you should confirm your S3 bucket actually serves your webpage. A quick manual upload proves the hosting configuration from the previous step is working.
- In your VS Code terminal, upload your HTML file to S3 by running this command:
aws s3 cp index.html s3://[[BUCKET_NAME="my-devops-site-yourname"]]/
What does this command do?
aws s3 cp copies a file from your local machine to your S3 bucket. The trailing / tells AWS to place the file at the root of the bucket.
You should see a line that says upload: followed by the file path and destination.
- Open your browser and visit your S3 website endpoint URL:
http://[[BUCKET_NAME="my-devops-site-yourname"]].s3-website-us-east-1.amazonaws.com
You should see your webpage with the heading "Hello from AWS S3!" and two paragraphs below it.
Seeing a 403 Forbidden or 404 error?
A 403 error means your bucket policy is not granting public read access. Go back to your bucket's Permissions tab in the AWS Console and confirm your bucket policy matches what you set in the previous step.
A 404 error means S3 cannot find index.html. Confirm the file uploaded successfully by checking the Objects tab in your bucket. Also verify the index document is set to index.html (case-sensitive) in bucket Properties > Static website hosting.
Help me fix my S3 website error
Your website is live and your code is on GitHub. Next up, you'll create an IAM user that gives GitHub Actions permission to deploy to your bucket automatically.
Create an IAM User for GitHub Actions
Your S3 website is live and you can see it working in the browser. But deploying means manually running a CLI command every single time you make a change.
To automate deployments, GitHub Actions needs permission to upload files to your S3 bucket. In this step, you'll create a dedicated IAM user with only the permissions needed to deploy.
In this step, get ready to:
- Create a dedicated IAM user for GitHub Actions deployments.
- Attach a least-privilege policy limiting access to your S3 bucket only.
- Store the credentials as GitHub repository secrets.
Create a dedicated IAM user
This user will exist solely for GitHub Actions to authenticate with AWS. It will have no console access and no permissions beyond what your deployment pipeline needs.
- In the AWS Console, search for IAM in the top search bar and open the IAM service.
- In the left navigation pane, click Users.
- Click Create user in the top right.
- In the User name field, enter github-actions-deployer.
- Leave the Provide user access to the AWS Management Console checkbox unchecked. This user only needs programmatic access.
- Click Next.
- On the Set permissions page, skip adding permissions for now. You will add a custom policy directly after creating the user.
- Click Next, then click Create user.
Why a dedicated user?
Creating a separate IAM user for deployments follows the principle of least privilege. If the credentials are ever compromised, the attacker can only write files to one S3 bucket. They cannot access your other AWS resources.
Attach a least-privilege policy
Now you'll give the github-actions-deployer user permission to manage files in your S3 bucket. You'll create the policy as a local JSON file first, then paste it into the AWS Console.
- In VS Code, create a new file called iam-deploy-policy.json in your project folder (the same folder that contains index.html).
- Paste the following policy into the file:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
What does this policy do?
This policy grants exactly four permissions, and only on your specific bucket:
- s3:PutObject allows uploading new files.
- s3:DeleteObject allows removing old files that no longer exist in your repo.
- s3:GetObject allows reading files (needed for sync comparisons).
- s3:ListBucket allows listing the bucket contents (needed for the --delete flag to know what to remove).
The two Resource lines scope these permissions to your bucket only. The first targets the bucket itself (for listing). The second targets all objects inside it (for put, get, and delete).
- Replace both instances of your-bucket-name with your actual S3 bucket name (e.g. my-devops-site-yourname).
- Save iam-deploy-policy.json.
Now attach this policy to your IAM user in the AWS Console.
- Back in the IAM console, click Users in the left navigation pane.
- Click github-actions-deployer to open the user details page.
- Select the Permissions tab.
- Click Add permissions, then select Create inline policy.
- In the Policy editor section, click JSON to switch to the JSON editor.
- Delete any placeholder text in the editor. Then copy the full contents of your iam-deploy-policy.json file and paste it in.
- Click Next.
- Under Policy name, enter S3DeployPolicy.
- Click Create policy.
You should see the policy appear under the Permissions tab for the github-actions-deployer user.
Don't see the policy listed?
Make sure you clicked Create policy at the end of the flow. If you navigated away before confirming, the policy was not saved. Repeat the steps starting from Add permissions.
Also check that you pasted valid JSON. If there is a syntax error, the console will show a warning banner at the top of the JSON editor.
help me attach an inline policy to my IAM user
Store credentials as GitHub secrets
GitHub Actions needs the access key ID and secret access key to authenticate as your IAM user. You'll store these as encrypted repository secrets so they never appear in your code.
- In the IAM console, click Users in the left navigation pane, then click github-actions-deployer.
- Select the Security credentials tab.
- Scroll down to the Access keys section and click Create access key.
- On the Access key best practices & alternatives page, select Other as the use case, then click Next.
- Click Create access key.
Copy your secret access key now
The secret access key is shown only once. After you close this page, you cannot retrieve it again. Copy both the Access key ID and the Secret access key and paste them somewhere temporary (like a text editor). You will add them to GitHub in the next few steps.
- Click Show to reveal the secret access key.
- Copy both the Access key ID and the Secret access key to a temporary location.
Now add both values as repository secrets in GitHub.
- Open your GitHub repository in your browser.
- Click Settings in the repository's top navigation bar.
- In the left sidebar, expand Secrets and variables under the Security section.
- Click Actions.
- Click New repository secret.
- In the Name field, enter AWS_ACCESS_KEY_ID.
- In the Secret field, paste your access key ID.
- Click Add secret.
- Click New repository secret again.
- In the Name field, enter AWS_SECRET_ACCESS_KEY.
- In the Secret field, paste your secret access key.
- Click Add secret.
You should now see both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY listed on the Actions secrets page. The values are encrypted and hidden.
Why use GitHub secrets?
GitHub encrypts secrets at rest and only exposes them to workflows at runtime. They never appear in logs or pull requests. This keeps your AWS credentials safe even if your repository is public.
Don't see the Settings tab?
The Settings tab only appears if you are the repository owner or have admin permissions. Make sure you are viewing your own repository, not a fork or someone else's repo.
help me find GitHub repository settings for secrets
Your IAM user is ready and GitHub has the credentials it needs. Next up, you'll build the GitHub Actions workflow that uses these credentials to deploy your site automatically on every push.
Build Your GitHub Actions CI/CD Pipeline
You have a website on S3, an IAM user with the right permissions, and credentials stored securely in GitHub. Now it's time to connect everything with automation.
A CI/CD pipeline watches your code repository for changes and automatically deploys them. In this step, you will create a GitHub Actions workflow file that syncs your code to S3 every time you push to your main branch.
In this step, get ready to:
- Create a GitHub Actions workflow file that automates deployment.
- Push the workflow and watch it deploy your site.
- Test the full cycle by editing your site and seeing it update automatically.
Create the workflow file
GitHub Actions reads workflow instructions from a special folder in your repository called .github/workflows. Any YAML file inside this folder becomes an automated pipeline.
- In your VS Code terminal, create the required folder structure by running this command:
mkdir -p .github/workflows
What does this command do?
The -p flag creates parent directories as needed. This single command creates both the .github folder and the workflows folder inside it.
- Confirm the folders exist by running:
ls .github/workflows
You should see an empty directory listing with no errors.
- Create a new file called deploy.yml inside .github/workflows/ by running:
code .github/workflows/deploy.yml
- Paste the following workflow configuration into deploy.yml:
name: Deploy to S3
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 sync . s3://[[BUCKET_NAME="your-bucket-name"]] --delete --exclude ".git/*" --exclude ".github/*"
What does this workflow do?
This file defines a complete deployment pipeline with three stages:
- on: push: branches: - main triggers the workflow every time you push commits to the main branch.
- actions/checkout@v4 downloads your repository code onto the runner machine so the next steps can access your files.
- aws-actions/configure-aws-credentials@v4 reads your GitHub secrets and sets up AWS authentication on the runner.
- aws s3 sync uploads your files to S3. The --delete flag removes files from S3 that no longer exist locally. The --exclude flags keep Git and GitHub Actions metadata out of your website.
- Save deploy.yml.
File not saving in the right place?
Make sure the file path is exactly .github/workflows/deploy.yml inside your project folder. Check the VS Code Explorer sidebar to confirm the file sits inside a workflows folder, which sits inside a .github folder.
help me fix my deploy.yml file path
✔️ Awesome, I've got everything!
Great. Double check you have saved deploy.yml before moving on.
ⓧ I'd like to double check the full code
Here is the complete .github/workflows/deploy.yml file:
name: Deploy to S3
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 sync . s3://[[BUCKET_NAME="your-bucket-name"]] --delete --exclude ".git/*" --exclude ".github/*"
Push and watch it deploy
Your workflow file is ready locally. The moment you push it to GitHub, the pipeline will trigger automatically because the workflow listens for pushes to the main branch.
- Stage and commit the new workflow file by running these commands:
git add .
git commit -m "Add GitHub Actions deploy workflow"
- Push to GitHub by running:
git push
What happens after you push?
GitHub detects the new .github/workflows/deploy.yml file and immediately starts running the workflow. The runner machine checks out your code, authenticates with AWS using your stored secrets, and syncs your files to S3.
- Open your GitHub repository in your browser.
- Click the Actions tab (under your repository name).
- Click the workflow run named Add GitHub Actions deploy workflow (this matches your commit message).
You should see a green checkmark next to the deploy job, indicating the pipeline completed successfully.
Seeing a red X instead of a green checkmark?
Click on the failed job to expand the logs. Common causes:
- Your bucket name in deploy.yml does not match the actual S3 bucket name you created. Check for typos.
- The GitHub secrets AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are missing or pasted incorrectly. Go to your repository Settings > Secrets and variables > Actions to verify they exist.
- The IAM policy does not include all four required actions (s3:PutObject, s3:DeleteObject, s3:GetObject, s3:ListBucket).
help me debug my failed GitHub Actions workflow
Test the full cycle
The pipeline worked once, but the real power is in the cycle: edit, push, and watch it go live automatically. Let's prove this works end to end.
- In VS Code, open index.html.
- Find this line:
<h1>Hello from AWS S3!</h1>
- Change the heading text to something new, so the line looks like this:
<h1>Hello from GitHub Actions!</h1>
- Save index.html.
- Commit and push your change by running:
git add .
git commit -m "Update heading to test pipeline"
git push
- Go back to the Actions tab in your GitHub repository.
- Click on the new workflow run named Update heading to test pipeline.
- Wait for the green checkmark to appear.
Once the pipeline finishes, refresh your S3 website endpoint URL in your browser. You should see your updated heading displayed on the page.
What just happened?
You just experienced the full DevOps cycle. You edited code locally, pushed to Git, and automation handled the rest. Your live website updated without you ever touching the AWS Console or running a deploy command manually.
Site not updating after the pipeline finishes?
Make sure you are refreshing the S3 website endpoint URL (the one starting with http:// and ending in .s3-website-), not the regular S3 console URL. Try a hard refresh with Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows) to bypass your browser cache.
my S3 website is not showing the updated content after a successful pipeline run
Secret mission
Upgrade to OIDC Authentication
Your pipeline works, but it relies on long-lived access keys stored as GitHub secrets. Professionals use a better approach. In this secret mission, you will replace those static credentials with OpenID Connect (OIDC) so that GitHub proves its identity to AWS using short-lived tokens. No more stored secrets.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. The S3 bucket with a few HTML files costs fractions of a cent per month, so there is no urgency to delete.
Resources you used:
- 1 S3 bucket (stores your website files).
- 1 IAM user with access key (or 1 IAM role if you completed the Secret Mission).
- 1 GitHub repository (free).
- Local project folder on your Desktop.
Keep everything running
No action needed. Choose this if you are still actively building or want to keep the site live as a portfolio piece.
- Your S3 bucket with a few HTML files costs less than $0.01/month with no traffic.
- Your GitHub repository is free to keep.
- The IAM user has no ongoing cost.
Pause - I'll come back to this later
There is nothing to pause for this project. S3 charges are based on storage and requests, and a static site with no traffic costs less than $0.01/month.
- You can leave everything as-is and return later with no meaningful cost.
- If you want to prevent public access temporarily, remove the bucket policy from the Permissions tab in your S3 bucket settings.
Delete - I don't want to use this again
Remove all project resources to bring costs to exactly $0. Follow each step below.
Delete your S3 bucket
- Delete your S3 bucket and all its contents by running this command:
aws s3 rb s3://[[BUCKET_NAME="your-bucket-name"]] --force
What does this command do?
The --force flag deletes all objects inside the bucket first, then removes the bucket itself.
Delete your IAM user
- Open the IAM console at https://console.aws.amazon.com/iam/.
- In the navigation pane, click Users.
- Select the checkbox next to github-actions-deployer.
- Click Delete at the top of the page.
- Type the username to confirm, then click Delete.
If you completed the Secret Mission and created an IAM role instead, navigate to Roles in the IAM console, select your role, and click Delete. Also remove the Identity provider entry under Identity providers in the IAM navigation pane.
Delete your GitHub repository
- Open your repository on GitHub.
- Click Settings.
- Scroll down to the Danger Zone section and click Delete this repository.
- Follow the confirmation prompts and type your repository name to confirm deletion.
Delete your local project folder
- Delete the local project folder from your Desktop by running the appropriate command for your operating system:
macOS
rm -rf ~/Desktop/my-devops-site
Windows
rmdir /s /q %USERPROFILE%\Desktop\my-devops-site
That's a Wrap!
That's a Wrap!
Congratulations! You just built a real DevOps pipeline from scratch. You now have a live website on AWS S3 that updates automatically every time you push code.
You've learned how to:
- Host a static website on Amazon S3, accessible to anyone with the public URL.
- Configure IAM permissions using least-privilege principles to securely connect GitHub to AWS.
- Build a CI/CD pipeline with GitHub Actions that deploys automatically on every push to main.
- Secret Mission: Upgraded to OIDC authentication, eliminating stored credentials entirely.
Ready to quiz yourself?