Create, Build & Deploy an Angular 9 Single-Page Application (SPA) on Amazon S3 (No Servers Required!)

Angular apps generate static assets (HTML, CSS & JS) when built. This means that they don’t need to be hosted on a full-fledged web/app server like Tomcat. They simply need to be served to the browser & client-side JS will take care of the rest. As such, S3’s ability to host static websites makes it an ideal choice for hosting Angular apps.

Now let’s see how to create, build & deploy an Angular app on S3.

Step 1 — Create App

Run Angular CLI’s ng new command to create an SPA:

> ng new single-page-app
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
CREATE single-page-app/README.md (1030 bytes)
CREATE single-page-app/.editorconfig (274 bytes)
CREATE single-page-app/.gitignore (631 bytes)
CREATE single-page-app/angular.json (3638 bytes)
CREATE single-page-app/package.json (1248 bytes)
CREATE single-page-app/tsconfig.json (489 bytes)
CREATE single-page-app/tslint.json (3125 bytes)
CREATE single-page-app/browserslist (429 bytes)
CREATE single-page-app/karma.conf.js (1027 bytes)
CREATE single-page-app/tsconfig.app.json (210 bytes)
CREATE single-page-app/tsconfig.spec.json (270 bytes)
CREATE single-page-app/src/favicon.ico (948 bytes)
CREATE single-page-app/src/index.html (299 bytes)
CREATE single-page-app/src/main.ts (372 bytes)
CREATE single-page-app/src/polyfills.ts (2835 bytes)
CREATE single-page-app/src/styles.css (80 bytes)
CREATE single-page-app/src/test.ts (753 bytes)
CREATE single-page-app/src/assets/.gitkeep (0 bytes)
CREATE single-page-app/src/environments/environment.prod.ts (51 bytes)
CREATE single-page-app/src/environments/environment.ts (662 bytes)
CREATE single-page-app/src/app/app.module.ts (314 bytes)
CREATE single-page-app/src/app/app.component.css (0 bytes)
CREATE single-page-app/src/app/app.component.html (25725 bytes)
CREATE single-page-app/src/app/app.component.spec.ts (969 bytes)
CREATE single-page-app/src/app/app.component.ts (219 bytes)
CREATE single-page-app/e2e/protractor.conf.js (808 bytes)
CREATE single-page-app/e2e/tsconfig.json (214 bytes)
CREATE single-page-app/e2e/src/app.e2e-spec.ts (648 bytes)
CREATE single-page-app/e2e/src/app.po.ts (301 bytes)
✔ Packages installed successfully.
    Successfully initialized git.

Step 2 — Build App

cd into single-page-app & build the app:

> ng build --prod
Compiling @angular/core : es2015 as esm2015
Compiling @angular/animations : es2015 as esm2015
Compiling @angular/compiler/testing : es2015 as esm2015
Compiling @angular/animations/browser : es2015 as esm2015
Compiling @angular/core/testing : es2015 as esm2015
Compiling @angular/common : es2015 as esm2015
Compiling @angular/platform-browser : es2015 as esm2015
Compiling @angular/common/http : es2015 as esm2015
Compiling @angular/common/testing : es2015 as esm2015
Compiling @angular/platform-browser-dynamic : es2015 as esm2015
Compiling @angular/platform-browser/testing : es2015 as esm2015
Compiling @angular/router : es2015 as esm2015
Compiling @angular/animations/browser/testing : es2015 as esm2015
Compiling @angular/common/http/testing : es2015 as esm2015
Compiling @angular/forms : es2015 as esm2015
Compiling @angular/platform-browser/animations : es2015 as esm2015
Compiling @angular/platform-browser-dynamic/testing : es2015 as esm2015
Compiling @angular/router/testing : es2015 as esm2015
Generating ES5 bundles for differential loading...
ES5 bundle generation complete.

chunk {0} runtime-es2015.1eba213af0b233498d9d.js (runtime) 1.45 kB [entry] [rendered]
chunk {0} runtime-es5.1eba213af0b233498d9d.js (runtime) 1.45 kB [entry] [rendered]
chunk {2} polyfills-es2015.690002c25ea8557bb4b0.js (polyfills) 36.1 kB [initial] [rendered]
chunk {3} polyfills-es5.9e286f6d9247438cbb02.js (polyfills-es5) 129 kB [initial] [rendered]
chunk {1} main-es2015.027a3ed6394e3f488e51.js (main) 138 kB [initial] [rendered]
chunk {1} main-es5.027a3ed6394e3f488e51.js (main) 160 kB [initial] [rendered]
chunk {4} styles.3ff695c00d717f2d2a11.css (styles) 0 bytes [initial] [rendered]
Date: 2020-06-15T14:19:07.190Z - Hash: 8cc03402db032b51b743 - Time: 71286ms

The dist folder now has the static assets:

> ls -R dist
single-page-app

dist/single-page-app:
3rdpartylicenses.txt
favicon.ico
index.html
main-es2015.027a3ed6394e3f488e51.js
main-es5.027a3ed6394e3f488e51.js
polyfills-es2015.690002c25ea8557bb4b0.js
polyfills-es5.9e286f6d9247438cbb02.js
runtime-es2015.1eba213af0b233498d9d.js
runtime-es5.1eba213af0b233498d9d.js
styles.3ff695c00d717f2d2a11.css

Step 3 — Configure Bucket

Add this bucket policy to the bucket:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{bucket-name}/*"
        }
    ]
}

Enable website hosting on the Properties tab of the S3 console:

Step 4 — Deploy to S3

Upload the contents of dist/single-page-app to S3 & open http://{bucket-name}.s3-website-us-east-1.amazonaws.com/index.html to see your app: