Host a Website on Amazon S3
Deploy a portfolio landing page to AWS S3 using the CLI.
Introduction
30 Second Summary
Every company with a website needs someone who knows how to put it on the internet. Cloud platforms like AWS power most of the web today, and knowing how to deploy even a simple site is the first step toward a career in cloud engineering.
In this project, you will deploy a personal portfolio landing page to Amazon S3 using the AWS CLI. You will create the site from scratch, configure an S3 bucket for static website hosting, and make it accessible to anyone on the internet via a public URL.
What You'll Build
A personal portfolio landing page hosted on AWS S3, deployed using the AWS CLI and configured to serve as a public static website.
By the end of this project, you'll have:
- A working AWS account with an IAM user configured for programmatic access.
- The AWS CLI installed and configured on your machine, ready for future cloud projects.
- A personal portfolio page live on the internet, hosted on an S3 bucket with static website hosting and a public bucket policy.
- Secret Mission: Add a custom error page and enable versioning so you can roll back changes to your site.
Are there any prerequisites?
None. This project includes full setup instructions for every tool you need, including AWS account creation and CLI installation. You do not need any prior cloud experience.
Before We Start
Before We Start...
Set Up Your AWS Account and CLI
Before you can deploy anything to the cloud, you need a way to talk to AWS from your computer. The AWS CLI lets you create, configure, and manage AWS resources directly from your terminal.
In this step, you will create an AWS account, set up an IAM user with the right permissions, install the AWS CLI, and connect everything together so your terminal can communicate with AWS.
In this step, get ready to:
- Create an AWS account and IAM user with access keys.
- Install and verify the AWS CLI on your machine.
- Configure the CLI to connect to your AWS account.
Create an AWS account and IAM user
An IAM user is a separate identity within your AWS account that has its own credentials and permissions. You will use this IAM user (instead of your root account) to interact with AWS services through the CLI.
- Go to https://aws.amazon.com and click Create an AWS Account.
- Follow the sign-up prompts to create your account with an email address, password, and payment method.
- Once signed in to the AWS Management Console, type IAM in the search bar at the top and select IAM from the results.
- In the left navigation pane, click Users.
- Click Create user.
- Enter a username (for example, cli-user) and click Next.
- On the permissions page, select Attach policies directly.
- Search for AmazonS3FullAccess and tick the checkbox next to it.
- Click Next, then click Create user.
Why create a separate IAM user?
Your root account has unrestricted access to everything in AWS. Using it for day-to-day tasks is risky because a leaked root credential could compromise your entire account.
An IAM user lets you follow the principle of least privilege. You grant only the permissions the user needs and nothing more.
Now you need to generate access keys so the CLI can authenticate as this user.
- Click on your new user's name to open their detail page.
- Select the Security credentials tab.
- Scroll down to Access keys and click Create access key.
- Select Command Line Interface (CLI) as the use case.
- Tick the confirmation checkbox at the bottom, then click Next.
- Click Create access key.
- Click Download .csv file and save the file to a secure location on your computer.
- Click Done.
Save your credentials now
This is your only opportunity to view or download the secret access key. If you lose it, you will need to delete the key and create a new one.
Finally, set up a billing alert so you are notified if any unexpected charges occur.
- In the AWS Console search bar, type Budgets and select AWS Budgets.
- Click Create budget.
- Select Use a template (simplified) and choose Monthly cost budget.
- Set the budgeted amount to 5 (dollars) and enter your email address for notifications.
- Click Create budget.
What does this budget do?
AWS will email you if your monthly spending approaches $5. This project stays well within the free tier, but a budget is a good safety net in case you accidentally leave resources running.
Install the AWS CLI
The AWS CLI is the command-line tool that lets you run AWS commands from your terminal. You will install version 2, which is the current supported release.
- Download the official macOS installer by opening this link in your browser:
- https://awscli.amazonaws.com/AWSCLIV2.pkg.
- Open the downloaded .pkg file and follow the installer prompts.
- Click Continue through each screen, then Install.
- Once the installation completes, open your terminal.
- Press Cmd+Space to open Spotlight Search.
- Type Terminal and press Enter to open it.
- Verify the installation by running this command:
aws --version
✔️ I see aws-cli/2.x.x
The AWS CLI is installed and ready to go. You should see output similar to:
aws-cli/2.34.48 Python/3.12.6 Darwin/23.6.0 source/arm64
The exact version numbers may differ slightly. As long as you see aws-cli/2 at the start, you are good to continue.
ⓧ I see aws-cli/1.x.x
You have an older version of the AWS CLI installed. Version 1 does not support all the features used in this project.
- Download and install the version 2 package from https://awscli.amazonaws.com/AWSCLIV2.pkg.
- Run through the installer prompts.
- Close and reopen your terminal, then run aws --version again.
- You should now see aws-cli/2 in the output.
ⓧ Command not found
The AWS CLI is not installed yet, or the installer did not complete successfully.
- Download the installer from https://awscli.amazonaws.com/AWSCLIV2.pkg.
- Double-click the .pkg file to run the installer.
- Follow the prompts and enter your macOS password when asked.
- Close your terminal completely and open a new terminal window.
- Run aws --version again to confirm the installation.
Still seeing command not found?
The installer places the aws binary at /usr/local/bin/aws. If your shell cannot find it, try running the full path:
/usr/local/bin/aws --version
If that works, your PATH variable does not include /usr/local/bin. Ask for help troubleshooting your PATH.
Configure the CLI
Now that the CLI is installed, you need to connect it to your AWS account. The aws configure command stores your credentials and default settings so every future command knows which account to talk to.
- Run the configuration wizard by entering this command:
aws configure
The CLI will prompt you for four pieces of information. Enter them one at a time:
- AWS Access Key ID: Paste the Access Key ID from the .csv file you downloaded earlier.
- AWS Secret Access Key: Paste the Secret Access Key from the same .csv file.
- Default region name: Type us-east-1.
- Default output format: Type json.
What is us-east-1?
AWS has data centres around the world, grouped into regions. us-east-1 (North Virginia) is the default region and the most commonly used for learning. It also simplifies S3 bucket creation because other regions require extra configuration.
Now verify that the CLI can successfully reach your AWS account.
- Test the connection by running this command:
aws sts get-caller-identity
You should see JSON output containing your account number, user ID, and ARN. It looks something like this:
{
"UserId": "AIDAEXAMPLEID",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/cli-user"
}
What does this output mean?
This confirms the CLI is authenticated. The Account field is your 12-digit AWS account number. The Arn field shows the full path to your IAM user.
Seeing an error instead of JSON?
If you see a message about invalid credentials or an expired token, double-check that you pasted the correct Access Key ID and Secret Access Key. You can re-run aws configure to enter them again.
If you accidentally deleted the .csv file and cannot remember the secret key, go back to the IAM console and create a new access key for your user.
Ask for help troubleshooting your CLI connection.
Your AWS account is ready and your CLI is connected. Next up, you will create the HTML and CSS files for your portfolio website.
Create Your Website Files
Your AWS account is configured and your CLI is ready to go. Now you need something to deploy.
Every static website is made of two ingredients: HTML for the content and structure, and CSS for the visual styling. You'll build a personal portfolio page that will become your first cloud-hosted site.
In this step, get ready to:
- Create a local project folder.
- Write an HTML portfolio landing page.
- Style the page with CSS.
Create a project folder
You need a dedicated folder to hold your website files. This keeps everything organized and makes it easy to upload the entire folder to S3 later.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir my-portfolio
cd my-portfolio
- Confirm you're inside the new folder by running:
pwd
You should see a path ending in my-portfolio, like /Users/yourname/Desktop/my-portfolio.
Seeing a different path?
Make sure you ran cd ~/Desktop before mkdir. If the folder ended up in the wrong place, delete it with rm -rf my-portfolio, navigate to your Desktop, and try again.
help me find my project folder
Now open the folder in VS Code so you can create your website files.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Select the my-portfolio folder on your Desktop, then click Open.
Don't have VS Code installed?
Download it for free from code.visualstudio.com. Any text editor works for this project, but VS Code makes file creation easy.
Write your HTML page
HTML defines the structure and content of your portfolio page. This file tells the browser what to display: headings, paragraphs, lists, and links.
- In the VS Code Explorer sidebar on the left, click the New File icon (it looks like a page with a plus sign).
- Type index.html as the filename and press Enter.
- Copy this code into your new index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Portfolio</title>
<!-- Link to the external stylesheet -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<!-- Page header with your title and subtitle -->
<header>
<h1>Hi, I'm a Cloud Engineer</h1>
<p class="subtitle">Building things in the cloud, one service at a time.</p>
</header>
<main>
<!-- About section introducing yourself -->
<section class="about">
<h2>About Me</h2>
<p>I'm learning cloud engineering with AWS. This site is hosted on Amazon S3 and deployed using the AWS CLI.</p>
</section>
<!-- Skills section listing what you're learning -->
<section class="skills">
<h2>What I'm Learning</h2>
<ul>
<li>AWS S3 and Static Hosting</li>
<li>IAM and Security</li>
<li>AWS CLI</li>
<li>Cloud Architecture</li>
</ul>
</section>
<!-- Links section with buttons to your profiles -->
<section class="links">
<h2>Links</h2>
<a href="https://github.com" class="btn">GitHub</a>
<a href="https://linkedin.com" class="btn">LinkedIn</a>
</section>
</main>
<footer>
<p>Deployed with AWS S3 🚀</p>
</footer>
</div>
</body>
</html>
What does this code do?
- The <head> section sets the page title and links to your CSS file for styling.
- The <header> displays your main heading and a subtitle.
- The about section introduces who you are.
- The skills section uses a list to show what you're learning.
- The links section provides buttons that link out to your profiles.
- Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Open the file in your browser by double-clicking index.html in Finder (macOS) or File Explorer (Windows).
You should see unstyled text on a white background, including the heading "Hi, I'm a Cloud Engineer" and a bullet list of skills.
Don't see any content?
Make sure you saved the file with the exact name index.html (not index.html.txt). Check that the file is inside your my-portfolio folder on the Desktop.
help me troubleshoot my HTML file
✔️ Awesome, I've got everything!
Great. Double check you've saved index.html and move on to styling.
ⓧ 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 Portfolio</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>Hi, I'm a Cloud Engineer</h1>
<p class="subtitle">Building things in the cloud, one service at a time.</p>
</header>
<main>
<section class="about">
<h2>About Me</h2>
<p>I'm learning cloud engineering with AWS. This site is hosted on Amazon S3 and deployed using the AWS CLI.</p>
</section>
<section class="skills">
<h2>What I'm Learning</h2>
<ul>
<li>AWS S3 and Static Hosting</li>
<li>IAM and Security</li>
<li>AWS CLI</li>
<li>Cloud Architecture</li>
</ul>
</section>
<section class="links">
<h2>Links</h2>
<a href="https://github.com" class="btn">GitHub</a>
<a href="https://linkedin.com" class="btn">LinkedIn</a>
</section>
</main>
<footer>
<p>Deployed with AWS S3 🚀</p>
</footer>
</div>
</body>
</html>
Style with CSS
CSS controls the visual appearance of your page. You'll add a dark background, centered layout, blue accent colors, and button styles to make your portfolio look professional.
- In the VS Code Explorer sidebar, click the New File icon again.
- Type style.css as the filename and press Enter.
- Add the page structure styles to style.css by copying this code:
/* Reset default browser spacing */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Dark background with centered content */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #0f172a;
color: #e2e8f0;
line-height: 1.6;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* Constrain the page width and center text */
.container {
max-width: 600px;
padding: 2rem;
text-align: center;
}
What does this code do?
- The * selector removes default margins and padding from all elements so you start with a clean slate.
- The body styles set a dark navy background (#0f172a) and use flexbox to center everything vertically and horizontally.
- The .container constrains the content to 600px wide so it doesn't stretch across the full screen.
- Save style.css and refresh your browser tab that has index.html open.
You should see your content centered on a dark navy background with light gray text.
Still seeing a white background?
Make sure your file is named exactly style.css (matching the <link> tag in your HTML). Both files must be in the same my-portfolio folder.
help me link my CSS file
- Add the typography styles below your existing code in style.css:
/* Space below the header */
header {
margin-bottom: 2rem;
}
/* Main heading style */
h1 {
font-size: 2.5rem;
color: #f8fafc;
margin-bottom: 0.5rem;
}
/* Muted subtitle text */
.subtitle {
font-size: 1.2rem;
color: #94a3b8;
}
/* Blue accent color for section headings */
h2 {
font-size: 1.4rem;
color: #38bdf8;
margin-bottom: 0.75rem;
}
/* Space between each section */
section {
margin-bottom: 2rem;
}
What does this code do?
- The h1 style makes your main heading large and bright white.
- The .subtitle uses a muted gray (#94a3b8) to create visual hierarchy.
- The h2 headings use a bright blue accent (#38bdf8) that draws the eye to each section title.
- Save style.css and refresh your browser.
You should see a large white heading, a gray subtitle, and blue section headings for "About Me," "What I'm Learning," and "Links."
- Add the component styles below your existing code in style.css:
/* Horizontal skill list with wrapping */
ul {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
/* Individual skill tags */
li {
background-color: #1e293b;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-size: 0.9rem;
}
/* Blue button links */
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #38bdf8;
color: #0f172a;
text-decoration: none;
border-radius: 0.5rem;
font-weight: 600;
margin: 0.25rem;
transition: background-color 0.2s;
}
/* Lighter blue on hover */
.btn:hover {
background-color: #7dd3fc;
}
/* Muted footer text */
footer {
margin-top: 2rem;
color: #64748b;
font-size: 0.9rem;
}
What does this code do?
- The ul and li styles turn the skills list into horizontal rounded tags instead of a vertical bullet list.
- The .btn class creates bright blue buttons with dark text, rounded corners, and a smooth hover effect.
- The footer uses a muted gray color so it stays visible without competing with your main content.
- Save style.css and refresh your browser.
You should see your skills displayed as neat rounded tags, two bright blue buttons for GitHub and LinkedIn, and a muted footer at the bottom.
Buttons not showing up as blue?
Check that the class="btn" attribute in your HTML matches the .btn selector in your CSS. Class names are case-sensitive. Also confirm there are no typos in the hex color #38bdf8.
help me fix my button styles
✔️ Awesome, I've got everything!
Great. Double check you've saved style.css and your page looks styled in the browser.
ⓧ I'd like to double check the full code
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #0f172a;
color: #e2e8f0;
line-height: 1.6;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
max-width: 600px;
padding: 2rem;
text-align: center;
}
header {
margin-bottom: 2rem;
}
h1 {
font-size: 2.5rem;
color: #f8fafc;
margin-bottom: 0.5rem;
}
.subtitle {
font-size: 1.2rem;
color: #94a3b8;
}
h2 {
font-size: 1.4rem;
color: #38bdf8;
margin-bottom: 0.75rem;
}
section {
margin-bottom: 2rem;
}
ul {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
li {
background-color: #1e293b;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-size: 0.9rem;
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #38bdf8;
color: #0f172a;
text-decoration: none;
border-radius: 0.5rem;
font-weight: 600;
margin: 0.25rem;
transition: background-color 0.2s;
}
.btn:hover {
background-color: #7dd3fc;
}
footer {
margin-top: 2rem;
color: #64748b;
font-size: 0.9rem;
}
Your website files are ready to go. Next up, you'll create an S3 bucket and configure it to host these files as a live website on the internet.
Create and Configure Your S3 Bucket
Your website files are ready, but they need a home on the internet. In AWS, that home is an S3 bucket.
A bucket is a container that holds your files in the cloud. In this step, you will create a bucket, open it up for public access, and tell AWS to serve its contents as a website.
In this step, get ready to:
- Create an S3 bucket using the AWS CLI.
- Disable Block Public Access so the world can reach your site.
- Enable static website hosting on the bucket.
Create the bucket
Every S3 bucket needs a globally unique name. No two buckets across all of AWS can share the same name. A good pattern is to combine your project name, your name, and the year.
- Create your S3 bucket by running this command (replace the bucket name with your own unique name):
aws s3 mb s3://[[BUCKET_NAME="my-portfolio-yourname-2024"]]
What does this command do?
- aws s3 mb stands for "make bucket." It creates a new S3 bucket in your default region (us-east-1).
- The s3:// prefix tells the CLI you are referencing an S3 resource.
- You should see a confirmation message like make_bucket: my-portfolio-yourname-2024 in your terminal.
Why do I have to enter my name in the bucket name?
S3 bucket names are globally unique across every AWS account in the world. Adding your name and a number makes it far less likely someone else has already taken the name.
Seeing a BucketAlreadyExists error?
- Someone else already owns that exact bucket name. Try adding extra numbers or your initials to make it unique.
- Make sure you replaced the placeholder with your own name. Do not use the example name exactly as written.
- help me fix a BucketAlreadyExists error when creating my S3 bucket
Disable Block Public Access
AWS locks down every new bucket by default. A feature called Block Public Access prevents anyone on the internet from reading your bucket's files. Since you want the world to see your website, you need to turn this off.
- Disable Block Public Access on your bucket by running this command:
aws s3api put-public-access-block \
--bucket [[BUCKET_NAME="my-portfolio-yourname-2024"]] \
--public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
What does this command do?
This command sets all four Block Public Access settings to false on your bucket. Here is what each one controls:
- BlockPublicAcls blocks new public ACLs from being applied.
- IgnorePublicAcls ignores any existing public ACLs.
- BlockPublicPolicy rejects bucket policies that grant public access.
- RestrictPublicBuckets restricts access to buckets with public policies.
Setting all four to false allows you to attach a public bucket policy in the next step.
- If the command succeeds, you will not see any output. No news is good news.
Getting an AccessDenied error?
- Check that your IAM user has the s3:PutBucketPublicAccessBlock permission. If you used AdministratorAccess in Step 1, this should already be covered.
- Make sure the bucket name in this command matches the exact name you used when creating the bucket.
- help me fix an AccessDenied error when disabling Block Public Access on my S3 bucket
Enable static website hosting
An S3 bucket can store files, but it does not serve them as web pages by default. You need to enable static website hosting so AWS knows to treat your bucket like a web server.
- Enable static website hosting on your bucket by running this command:
aws s3 website s3://[[BUCKET_NAME="my-portfolio-yourname-2024"]]/ --index-document index.html --error-document index.html
What does this command do?
- --index-document index.html tells S3 which file to serve when someone visits the root URL of your site.
- --error-document index.html tells S3 which file to show when a visitor requests a page that does not exist.
Once enabled, your bucket gets a public website endpoint URL. For us-east-1, the format is:
http://[[BUCKET_NAME="my-portfolio-yourname-2024"]].s3-website-us-east-1.amazonaws.com
- If the command succeeds, you will not see any output.
- Note down your website endpoint URL. You will need it in the next step to visit your live site. The format is http://your-bucket-name.s3-website-us-east-1.amazonaws.com.
Not sure if it worked?
- The aws s3 website command produces no output on success. If you see no error, the configuration was applied.
- Double check that you included the trailing / after your bucket name in the s3:// URI.
- help me verify that static website hosting is enabled on my S3 bucket
Your bucket is created, public access is unlocked, and static hosting is on. Next up, you will upload your files and make your site live.
Deploy Your Website
Your S3 bucket is configured and ready to serve a website. Now it is time to put your portfolio files inside it and make them accessible to anyone on the internet.
In this step, you will upload your HTML and CSS files to the bucket using the AWS CLI. Then you will attach a bucket policy that grants public read access so browsers can load your page.
In this step, get ready to:
- Upload your website files to the S3 bucket.
- Add a bucket policy granting public read access.
- Visit your live site on the internet.
Upload your files to S3
Your website files exist locally in the my-portfolio folder. The aws s3 cp command copies files from your machine into your S3 bucket.
- Make sure you are in your my-portfolio folder by running this command:
cd ~/Desktop/my-portfolio
- Upload all files in the current folder to your S3 bucket by running this command:
aws s3 cp . s3://[[BUCKET_NAME="your-bucket-name"]] --recursive
What does this command do?
- The . refers to the current directory, which contains your index.html and style.css files.
- The s3:// prefix tells the CLI you are targeting an S3 bucket.
- The --recursive flag copies all files in the directory, not just a single file.
- You should see output confirming each file was uploaded to your bucket.
Not seeing upload confirmations?
- Make sure you are in the my-portfolio folder. Run ls to check that index.html and style.css are listed.
- Double check that you replaced the bucket name placeholder with the actual name you chose in the previous step.
- If you see an access denied error, confirm your AWS CLI is configured correctly by running aws sts get-caller-identity.
My upload command is failing. What should I check?
Add a bucket policy for public access
Your files are in the bucket, but nobody can read them yet. By default, S3 objects are private. A bucket policy is a JSON document that defines who can access your bucket and what they can do.
- Attach a public read policy to your bucket by running this command:
aws s3api put-bucket-policy --bucket [[BUCKET_NAME="your-bucket-name"]] --policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[[BUCKET_NAME="your-bucket-name"]]/*"
}
]
}'
What does this policy do?
- "Effect": "Allow" grants permission (as opposed to denying it).
- "Principal": "*" means anyone on the internet. The asterisk is a wildcard for all users.
- "Action": "s3:GetObject" allows reading (downloading) objects. It does not allow writing or deleting.
- "Resource" specifies which objects the policy applies to. The /* at the end means all objects in the bucket.
This is the minimum permission needed for a public website. Visitors can view your pages but cannot modify or delete anything.
- This command produces no output on success. If your terminal returns to the prompt without an error message, the policy was applied correctly.
Seeing an access denied error?
- This usually means Block Public Access is still enabled on the bucket. Confirm you disabled it in the previous step.
- Make sure the bucket name in --bucket matches the bucket name inside the Resource ARN exactly.
- Check that your JSON has no typos. A missing quote or bracket will cause a parsing error.
Why is my bucket policy being denied?
Visit your live site
Your files are uploaded and the policy allows public reads. Your website is live.
- Open your browser and paste in your S3 website endpoint URL:
http://[[BUCKET_NAME="your-bucket-name"]].s3-website-us-east-1.amazonaws.com
- You should see your portfolio page with a dark background, your name as a heading, skill tags, and GitHub/LinkedIn buttons styled in blue.
Where does this URL come from?
When you enabled static website hosting in the previous step, AWS created this endpoint for your bucket. The format for the us-east-1 region is always http://bucket-name.s3-website-us-east-1.amazonaws.com.
Seeing a 403 Forbidden error?
- The bucket policy was not applied successfully. Run the aws s3api put-bucket-policy command again and check for JSON formatting errors.
- Confirm your bucket name in the URL matches the actual bucket name exactly. Typos will point to a non-existent bucket.
- If you see a 404 error instead, check that your index.html file was uploaded. Run aws s3 ls s3://your-bucket-name to list the bucket contents.
My S3 website is showing an error page instead of my portfolio. What should I check?
Your portfolio is live on the internet. In the next step, you will make a change to your site and redeploy it to see how fast updates work with the CLI.
Update and Redeploy
Your website is live on the internet. In a real project, you would update your site constantly as you add new skills, projects, and experience.
In this step, you will make a change to your HTML, then use aws s3 sync to push only the changed files back to your bucket. This is faster than re-uploading everything because sync compares your local files against what is already in S3 and only transfers what has changed.
In this step, get ready to:
- Edit your HTML to add new content.
- Redeploy the updated file using aws s3 sync.
- Verify your changes are live on the internet.
Make a change to your HTML
You can change anything on your site. For this walkthrough, you will update the subtitle text in your index.html to practise the redeploy workflow.
- Open index.html in your code editor.
- Find this line inside the <header> section:
<p class="subtitle">Building things in the cloud, one service at a time.</p>
- Replace that line with your own text. For example:
<p class="subtitle">Deploying to the cloud and loving every minute.</p>
- Save index.html.
Make it yours
Feel free to change more than just the subtitle. Add a new <section>, update your skills list, or rewrite the "About Me" paragraph. Any saved change to the file will work for the next step.
Redeploy with sync
Instead of re-uploading every file, aws s3 sync compares your local folder to the bucket and uploads only the files that have changed.
- Make sure your terminal is inside the my-portfolio folder, then sync your files to S3 by running this command:
aws s3 sync . s3://[[BUCKET_NAME="my-portfolio-yourname-2024"]]
What does this command do?
- aws s3 sync tells the CLI to compare a local directory with an S3 location.
- . refers to the current directory (your my-portfolio folder).
- Only files that are new or modified get uploaded. Unchanged files are skipped entirely.
You should see output showing that only index.html was uploaded. The style.css file did not change, so it is not listed.
Why sync instead of cp?
The aws s3 cp --recursive command re-uploads every file regardless of whether it changed. For a two-file site the difference is negligible.
On larger projects with hundreds of assets, sync saves significant time and bandwidth because it only transfers what is new or modified.
Seeing an upload error?
- Double-check that your terminal's working directory is the my-portfolio folder. Run ls and confirm you see index.html and style.css.
- Make sure the bucket name in the command matches the bucket you created earlier.
Help me troubleshoot my aws s3 sync command. I'm trying to upload updated files to my S3 bucket but getting an error.
Verify the update in your browser
- Open (or refresh) your S3 website endpoint in your browser:
http://[[BUCKET_NAME="my-portfolio-yourname-2024"]].s3-website-us-east-1.amazonaws.com
You should see your updated subtitle (or whichever content you changed) reflected on the live page.
Still seeing the old content?
- Try a hard refresh. Press Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows) to bypass the browser cache.
- Confirm you saved index.html locally before running the sync command.
My S3 website is still showing old content after I ran aws s3 sync. Help me figure out why the update isn't appearing.
You now have a complete deployment workflow: edit locally, sync to S3, and your changes go live in seconds. This is the foundation of every cloud deployment pipeline.
Secret mission
Add a Custom Error Page and Enable Versioning
Real websites need graceful error handling and the ability to roll back mistakes. In this secret mission, you will add a custom 404 page and enable bucket versioning so you can restore previous versions of your site.
Clean Up Your Resources
Clean Up Your Resources
This project runs on AWS S3, which is covered by the AWS Free Tier for 12 months after account creation. Your bucket costs nothing while you stay within the free tier limits (5 GB storage, 20,000 GET requests per month).
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- S3 bucket with static website hosting enabled.
- Website files uploaded to the bucket.
- Local project folder on your Desktop.
- AWS CLI installed on your machine.
- AWS credentials stored in ~/.aws/.
Keep everything running
No action needed. Choose this if you plan to keep iterating on your portfolio site or use this bucket for future projects.
- Your site stays live at no cost under the AWS Free Tier.
- The AWS CLI remains installed and configured for future cloud projects.
- After your 12-month free tier expires, a small static site typically costs under $0.05 per month.
Pause - I'll come back to this later
S3 does not have a pause or stop feature. Your bucket simply exists and serves files when requested.
- Within the free tier, there is no cost difference between an active bucket and an idle one.
- If you want to stop serving the site publicly, you can remove the bucket policy to block public access. Your files stay in the bucket for when you return.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each step below.
- Delete the S3 bucket and all its contents by running this command:
- Delete your local project folder:
- (Optional) Uninstall the AWS CLI and remove stored credentials:
Delete the S3 bucket:
aws s3 rb s3://[[BUCKET_NAME="your-unique-bucket-name"]] --force
What does this command do?
The --force flag empties the bucket first, then deletes it. This removes all uploaded files and the bucket itself in one command.
Delete local project files (macOS/Linux):
rm -rf ~/Desktop/my-portfolio
Delete local project files (Windows):
rmdir /s /q %USERPROFILE%\Desktop\my-portfolio
(Optional) Uninstall the AWS CLI:
sudo rm /usr/local/bin/aws
sudo rm /usr/local/bin/aws_completer
sudo rm -rf /usr/local/aws-cli
(Optional) Remove stored AWS credentials and configuration:
rm -rf ~/.aws/
Removing credentials is permanent
Deleting the ~/.aws/ folder removes your Access Key ID, Secret Access Key, and region configuration. If you plan to use any AWS service in the future, you will need to run aws configure again.
That's a Wrap!
That's a Wrap!
You just deployed your first website to the cloud. You took a simple HTML page from your local machine to a live public URL, all using the AWS CLI.
You've learned how to:
- Set up an AWS account with IAM security and configured the AWS CLI for programmatic access to cloud services.
- Created an S3 bucket and configured it for static website hosting with a public bucket policy.
- Deployed and updated a portfolio landing page to a live public URL using aws s3 cp and aws s3 sync.
- Secret Mission: Added a custom error page and enabled versioning for rollback capability.
Ready to quiz yourself?