> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Building and Deploying Angular Apps on Upsun

> How to build and deploy an Angular application on Upsun.

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["anacidre"], date: "2025-12-03T00:00:00+00:00", image: "/images/posts/tutorials/angular-and-upsun/angular-and-upsun.webp" }} />

Angular 21 has just been released, bringing performance improvements and a more modern, streamlined developer experience. The release focuses on speed, accessibility, and a smoother path toward fully zoneless Angular applications. Check out [this blog post](https://blog.angular.dev/announcing-angular-v21-57946c34f14b) by the Angular team to get full insights!

To celebrate this release we’ve created this guide that walks you through how to deploy an Angular application on Upsun using best practices which includes standalone components, optimized production builds and Upsun’s single-runtime configuration.

## **Why Angular and Upsun Go Hand in Hand**

Angular’s build-first, static-output architecture aligns naturally with Upsun’s automated deployment platform.

**Modern Angular Builds + Reproducible Upsun Environments:** Angular 21’s optimized Vite-powered builds run in clean, isolated containers on Upsun. You get predictable output, consistent Node versions, and fully repeatable deployments.

**Angular SPA Output + Upsun Zero-Config Static Hosting:** Upsun automatically serves Angular’s generated `dist/` output (automatic HTTPS, global caching, compression, instant rollbacks).

**Angular Preview Workflows + Upsun On-Demand Environments:** Every Git branch deployed on Upsun becomes its own environment with its own URL, perfect for:

* Staging UI updates
* Reviewing component changes
* Demos and feedback rounds
* Validating design improvements

**Optional Backend + Upsun Multi-App Architecture.** If your Angular app needs an API layer, Upsun lets you:

* Add the language of your choice for your backend
* Use managed databases
* Auto-connect services internally
* Inject env variables per environment
* Upsun also handles all service wiring.

## **Prerequisites**

Before you begin, make sure the following requirements are met:

* **You already have an Upsun account.**\
  If you don’t have an Upsun account, create one here: [https://console.upsun.com/](https://console.upsun.com/)
* **You have an Angular project ready.**\
  If you don’t have an Angular project, you can [follow the official Angular setup guide](https://angular.dev/installation) to create a new project.
* **Your Angular application builds successfully on your local machine.**\
  You can verify this by checking that a **dist/\<app-name>** directory is created after running:

```shell {filename="Terminal"} theme={null}
ng build 
```

* **The Upsun CLI is installed locally.**\
  If you haven’t installed it yet, you can install it following [these instructions](https://docs.upsun.com/administration/cli.html).

## **Let’s get started!**

### 1. Create project

First, you need to create a project with the Upsun CLI (you can also do this through the [Upsun Console](https://console.upsun.com/)).

```shell {filename="Terminal"} theme={null}
upsun project:create
```

You will be prompted to answer:

* Add project title
* Choose project region
* Define default branch

Then you will be prompted to set the new project as the remote for this repository directory. Select Yes.

Finally a project cost will be given to you to accept.

### 2. Initialize your project

From the root of your Angular project, run:

```shell {filename="Terminal"} theme={null}
upsun project:init
```

This command:

* Detects your project stack
* Creates the `.upsun` folder
* Generates a starter `config.yaml`
* Connects your project to Upsun (if not already connected)

You’ll now have:

```shell {filename="Terminal"} theme={null}
.
├── .upsun/
│   └── config.yaml
└── src/
```

### 3. Configure Angular in .upsun/config.yaml

Open the generated file and update it for an Angular static deployment:

```yaml {filename=".upsun/config.yaml"} theme={null}
# Upsun configuration, generated using AI at: 2025-11-21T13:10:37+01:00
# AI can make mistakes. Please modify this file to suit your needs.
applications:
  angularupsundemo:
    type: nodejs:24

    build:
      flavor: none
      # For Angular CLI cache and build output
    mounts:
      /.angular:
        source: storage
        source_path: angular

    hooks:
      build: |
        set -ex
        npm install 
        npm run build

    web:
      commands:
        start: npm start  -- --port $PORT --host 0.0.0.0 --allowed-hosts

      locations:
        /:
          root: public
          passthru: true
          allow: false

        '/(api|health)':
          passthru: true

    
routes:
  https://{default}/:
    type: upstream
    upstream: angularupsundemo:http
```

Everything is handled in a single file. Mounts define the required file system structure and state management that the running Node.js/Angular environment needs on the Upsun platform, ensuring crucial directories like /.angular are persisted and available at runtime.

Without it, the Angular build will fail.

### 4. Push Your Code

Once your config is ready add the files to Git and add a commit message:

```shell {filename="Terminal"} theme={null}
git add .
git commit -m "Add Upsun Configuration"
```

And lastly push your project to Upsun

```shell {filename="Terminal"} theme={null}
upsun push
```

Upsun immediately:

1. Builds your Angular app
2. Provisions the environment
3. Deploys the output
4. Provides a live URL

Your Angular app is now deployed.

## **Feature Environments**

One of my favorite features of Upsun is [environments](https://docs.upsun.com/environments.html), as it states in our documentation “You can think of other environments as copies of your live site where you can run tests without worrying about damaging anything in production.” Let's see how it works.

We start by creating a new branch:

```shell {filename="Terminal"} theme={null}
git checkout -b feature/test
```

We will build a feature by generating a new component to test this out.

```shell {filename="Terminal"} theme={null}
ng generate component components/test-feature
```

Now, add the new component to the app.ts file

```ts {filename="app.ts"} theme={null}
import { Component, signal } from '@angular/core';
import { TestFeature } from './components/test-feature/test-feature';

@Component({
  selector: 'app-root',
  imports: [TestFeature],
  template: `
    <main class="content">
      <app-test-feature [title]="title()" />
    </main>
  `,
  styleUrl: './app.css'
})
export class App {
  protected readonly title = signal('upsun-angular-demo');
}
```

Add to GitHub and push to Upsun:

```shell {filename="Terminal"} theme={null}
git add .
git commit -m "add new feature"
upsun push feature/test
```

Upsun deploys it automatically with a unique URL. Ideal for:

* UI reviews
* QA
* Demos
* Experimentation

No manual setup. That’s it!

## Final thoughts

Angular 21 modernizes the framework with faster builds, better accessibility tools and a fully zoneless future. Upsun complements this perfectly by removing infrastructure complexity and automating deployment end-to-end.

Together, they give you:

* Predictable builds
* Automated environments
* Global static hosting
* Effortless previews
* Zero maintenance infrastructure

Angular + Upsun makes deployment simple, scalable and enjoyable.

If you'd like to explore a complete working example, including the Angular 21 app, Upsun configuration and deployment setup, you can find it [here](https://github.com/upsun/demo-angular-upsun)
