Deploying an Aurelia App
Aurelia is a modern, open-source front-end framework for building browser-based applications with powerful data-binding, a modular architecture, and a strong focus on web standards. It provides developers with the tools to build scalable, maintainable single-page applications (SPAs) with intuitive APIs and comprehensive testing support. Aurelia’s component-driven architecture and excellent documentation make it ideal for large-scale applications requiring deep customization.
This comprehensive guide walks through deploying an Aurelia application to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to create and structure an Aurelia project, build components and services, configure environment variables, implement security best practices, set up monitoring, deploy custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Aurelia application running on Klutch.sh’s global infrastructure with automatic HTTPS and optimized performance.
Prerequisites
- Node.js & npm (version 18+) – Download Node.js
- Aurelia CLI installed globally (install with
npm install -g aurelia-cli) - Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
- Klutch.sh account with access to the dashboard at klutch.sh/app
- Basic knowledge of JavaScript/TypeScript, HTML, CSS, and the Node.js ecosystem
Getting Started: Create an Aurelia App
1. Install Aurelia CLI
Install the Aurelia CLI globally:
npm install -g aurelia-cli2. Create a New Aurelia Project
Create a new Aurelia application:
au new my-aurelia-appcd my-aurelia-appnpm installThe CLI will guide you through configuration options for project setup.
3. Project Structure
A typical Aurelia project structure looks like:
my-aurelia-app/├── src/│ ├── resources/│ │ ├── attributes/│ │ ├── binding-behaviors/│ │ ├── components/│ │ └── value-converters/│ ├── services/│ │ ├── api.ts│ │ ├── auth.ts│ │ └── ...│ ├── app.html│ ├── app.ts│ ├── main.ts│ └── ...├── aurelia.json├── package.json├── tsconfig.json├── Dockerfile└── README.md4. Run the Development Server
Start the Aurelia development server:
au run --watchNavigate to http://localhost:8080 in your browser. The app will automatically reload as you make changes.
5. Sample Component
Create a new component:
import { useResource } from "aurelia";import { ArticleService } from "../../services/article-service";
export interface Article { id: string; title: string; content: string; author: string; createdAt: string;}
export class ArticleListComponent { articles: Article[] = []; loading = true; error = null;
constructor(private articleService: ArticleService) {}
async attached() { try { this.articles = await this.articleService.getArticles(); } catch (err) { this.error = err; } finally { this.loading = false; } }}6. Sample Component Template
Create the corresponding template:
<template> <div class="article-list"> <h2>Articles</h2>
<div if.bind="loading" class="loading"> Loading articles... </div>
<div if.bind="error" class="error"> Failed to load articles </div>
<div if.bind="!loading && articles.length === 0" class="no-articles"> No articles found </div>
<div repeat.for="article of articles" class="article-card"> <h3>${article.title}</h3> <p>${article.content}</p> <small>By ${article.author} - ${article.createdAt | dateFormat}</small> </div> </div></template>7. Sample Service
Create an API service:
import { HttpClient } from "@aurelia/fetch-client";import { IContainer, injection } from "aurelia";
export interface Article { id: string; title: string; content: string; author: string; createdAt: string;}
@injection(HttpClient)export class ArticleService { private apiUrl = import.meta.env.VITE_API_URL || "https://api.example.com";
constructor(private http: HttpClient) {}
async getArticles(): Promise<Article[]> { const response = await this.http.fetch(`${this.apiUrl}/articles`); if (!response.ok) { throw new Error(`Failed to fetch articles: ${response.statusText}`); } return await response.json(); }
async getArticle(id: string): Promise<Article> { const response = await this.http.fetch(`${this.apiUrl}/articles/${id}`); if (!response.ok) { throw new Error(`Failed to fetch article: ${response.statusText}`); } return await response.json(); }
async createArticle(data: Omit<Article, "id" | "createdAt">): Promise<Article> { const response = await this.http.fetch(`${this.apiUrl}/articles`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`Failed to create article: ${response.statusText}`); } return await response.json(); }
async updateArticle(id: string, data: Partial<Article>): Promise<Article> { const response = await this.http.fetch(`${this.apiUrl}/articles/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`Failed to update article: ${response.statusText}`); } return await response.json(); }
async deleteArticle(id: string): Promise<void> { const response = await this.http.fetch(`${this.apiUrl}/articles/${id}`, { method: "DELETE", }); if (!response.ok) { throw new Error(`Failed to delete article: ${response.statusText}`); } }}8. Root Component
Update your root component:
export class App { message = "Welcome to Aurelia on Klutch.sh";}<template> <div class="container"> <header> <h1>Aurelia Application</h1> <p>${message}</p> </header>
<main> <nav> <ul> <li><a href="#home">Home</a></li> <li><a href="#articles">Articles</a></li> <li><a href="#about">About</a></li> </ul> </nav>
<router-outlet></router-outlet> </main>
<footer> <p>© 2024 My Aurelia App. All rights reserved.</p> </footer> </div></template>9. Configure Environment Variables
Create a .env file for development:
VITE_API_URL=http://localhost:3000/apiVITE_SITE_NAME=My Aurelia AppAccess these in your Aurelia app:
const apiUrl = import.meta.env.VITE_API_URL;const siteName = import.meta.env.VITE_SITE_NAME;Local Production Build Test
Before deploying, test the production build locally:
au build --env prodau runVisit http://localhost:8080 to verify that your app renders correctly in production mode.
Deploying with Nixpacks
Nixpacks automatically detects your Node.js/Aurelia application and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for Aurelia applications.
Prerequisites for Nixpacks Deployment
- Your Aurelia project pushed to a GitHub repository
- Valid
package.jsonwith build and start scripts - No
Dockerfilein the repository root (if one exists, Klutch.sh will use Docker instead)
Steps to Deploy with Nixpacks
-
Push Your Aurelia Project to GitHub
Initialize and push your project to GitHub if you haven’t already:
Terminal window git initgit add .git commit -m "Initial Aurelia app"git branch -M maingit remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.gitgit push -u origin main -
Log In to Klutch.sh Dashboard
Go to klutch.sh/app and sign in with your GitHub account.
-
Create a Project
Navigate to the Projects section and create a new project for your Aurelia app.
-
Create an App
Click “Create App” and select your GitHub repository.
-
Select the Branch
Choose the branch you want to deploy (typically
main). -
Configure Traffic Type
Select HTTP as the traffic type for Aurelia (a web framework serving HTML/assets).
-
Set the Internal Port
Set the internal port to
8080– this is the port where the Aurelia dev server listens and should be used for Node.js-based production servers. -
Add Environment Variables (Optional)
Add any environment variables your Aurelia app requires:
NODE_ENV=productionVITE_API_URL=https://api.example.comVITE_SITE_NAME=My Aurelia AppIf you need to customize the Nixpacks build or start command, use these environment variables:
BUILD_COMMAND: Override the default build command (e.g.,npm run build)START_COMMAND: Override the default start command for serving the built app
-
Configure Compute Resources
Select your region, compute size, and number of instances based on expected traffic.
-
Deploy
Click “Create” to start the deployment. Nixpacks will automatically build and deploy your Aurelia app. Your app will be available at a URL like
https://example-app.klutch.sh.
Deploying with Docker
For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.
Creating a Dockerfile for Aurelia
Create a Dockerfile in the root of your Aurelia project:
# === Build stage ===FROM node:20-alpine AS builder
WORKDIR /app
RUN npm install -g aurelia-cli
COPY package*.json ./RUN npm install
COPY . .RUN npm run build
# === Runtime stage ===FROM node:20-alpine AS runtime
WORKDIR /app
RUN npm install -g serve
COPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/package*.json ./
ENV NODE_ENV=productionENV PORT=8080EXPOSE 8080
CMD ["serve", "-s", "dist", "-l", "8080"]Dockerfile Notes
- Builder stage: Installs Aurelia CLI and builds your Aurelia app for production into the
distfolder. - Runtime stage: Uses a lightweight Node.js Alpine image with the
serveutility to serve your compiled Aurelia application. - Port: The
PORTenvironment variable is set to8080, which is the recommended internal port for Aurelia. - Multi-stage build: Reduces final image size by excluding build tools and dev dependencies from the runtime container.
Alternative Dockerfile for Static Hosting (Nginx)
If you prefer to serve your Aurelia app with Nginx:
# === Build stage ===FROM node:20-alpine AS builder
WORKDIR /app
RUN npm install -g aurelia-cli
COPY package*.json ./RUN npm install
COPY . .RUN npm run build
# === Runtime stage (Nginx) ===FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Note: With the Nginx Dockerfile, set the internal port to 80 instead of 8080.
Steps to Deploy with Docker
-
Create a Dockerfile
Add the Dockerfile (shown above) to the root of your Aurelia repository.
-
Test Locally (Optional)
Build and test the Docker image locally:
Terminal window docker build -t aurelia-app:latest .docker run -p 8080:8080 aurelia-app:latestVisit http://localhost:8080 to verify.
-
Push to GitHub
Commit and push the Dockerfile and your code:
Terminal window git add Dockerfilegit commit -m "Add Dockerfile for production deployment"git push origin main -
Create an App in Klutch.sh
Go to klutch.sh/app, navigate to “Create App”, and select your repository.
-
Configure the App
- Traffic Type: Select HTTP
- Internal Port: Set to
8080(or80if using the Nginx Dockerfile) - Environment Variables: Add any required runtime variables
-
Deploy
Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your app. Your app will be available at
https://example-app.klutch.sh.
Environment Variables
Define all environment variables in the Klutch.sh dashboard. Here’s a recommended set for production:
NODE_ENV=productionVITE_API_URL=https://api.example.comVITE_SITE_NAME=My Aurelia AppVITE_ANALYTICS_KEY=your-analytics-keyAccessing Environment Variables in Aurelia
Access environment variables in your Aurelia components and services using import.meta.env:
const apiUrl = import.meta.env.VITE_API_URL;const siteName = import.meta.env.VITE_SITE_NAME;
// In templatesexport class MyComponent { apiUrl = import.meta.env.VITE_API_URL;}Only variables prefixed with VITE_ are exposed to the browser. Keep sensitive data like API keys in server-side only or use server endpoints.
Persistent Storage
If your Aurelia application needs to store files or user-generated content, you can use persistent volumes in Klutch.sh.
Adding Persistent Volumes
- In the Klutch.sh dashboard, go to your app’s Volumes section.
- Click Add Volume.
- Set the mount path (e.g.,
/data,/uploads, or/var/www/uploads). - Set the size (e.g.,
1 GiB,10 GiB). - Save and redeploy your app.
Example: Using Persistent Storage with a Backend Service
If you’re using a backend API for file uploads:
@injection(HttpClient)export class UploadService { private apiUrl = import.meta.env.VITE_API_URL;
constructor(private http: HttpClient) {}
async uploadFile(file: File): Promise<{ path: string }> { const formData = new FormData(); formData.append("file", file);
const response = await this.http.fetch(`${this.apiUrl}/upload`, { method: "POST", body: formData, });
if (!response.ok) { throw new Error("File upload failed"); }
return await response.json(); }}Security Best Practices
1. HTTPS/SSL Enforcement
Klutch.sh automatically provides HTTPS for all deployed apps. Ensure your Aurelia app redirects HTTP to HTTPS by configuring your server appropriately.
2. Content Security Policy
Implement CSP headers in your backend or server configuration:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';3. Environment Variable Protection
Never commit sensitive data to version control. Use Klutch.sh environment variables for:
VITE_API_KEY=your-secret-keyVITE_DATABASE_URL=postgresql://...Only expose variables prefixed with VITE_ to the browser.
4. Input Validation
Validate all user inputs in your Aurelia components:
export class FormComponent { name: string = ""; email: string = "";
onSubmit() { if (!this.name || !this.email) { return; // Validation failed }
// Validate email format const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(this.email)) { return; // Invalid email }
// Submit form }}5. CORS Configuration
Configure CORS in your backend API if needed:
Access-Control-Allow-Origin: https://example-app.klutch.sh, https://custom-domain.comAccess-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Headers: Content-Type, Authorization6. Dependency Security
Regularly audit and update dependencies:
npm auditnpm audit fixnpm update7. Secure API Communication
Use HTTPS for all API calls and implement proper authentication:
@injection(HttpClient)export class ApiService { constructor(private http: HttpClient) { // Configure default headers for authentication this.http.configure(config => { config.withDefaults({ headers: { "Authorization": `Bearer ${this.getToken()}`, } }); }); }
private getToken(): string { return localStorage.getItem("authToken") || ""; }}Monitoring and Logging
Health Check Endpoint
Implement a health check endpoint in your backend API:
// Backend exampleapp.get("/health", (req, res) => { res.json({ status: "healthy", timestamp: new Date().toISOString(), uptime: process.uptime(), });});Client-Side Error Handling
Implement comprehensive error handling in your Aurelia app:
@injection(LoggerService)export class GlobalErrorHandler { constructor(private logger: LoggerService) { window.addEventListener("error", (event) => { this.logger.error("Global error", { message: event.message, filename: event.filename, lineno: event.lineno, }); });
window.addEventListener("unhandledrejection", (event) => { this.logger.error("Unhandled promise rejection", { reason: event.reason, }); }); }}Structured Logging
Implement structured logging for production monitoring:
export class LoggerService { log(level: string, message: string, data?: any) { const entry = { timestamp: new Date().toISOString(), level, message, data, }; console.log(JSON.stringify(entry)); }
info(message: string, data?: any) { this.log("info", message, data); }
warn(message: string, data?: any) { this.log("warn", message, data); }
error(message: string, data?: any) { this.log("error", message, data); }}Custom Domains
To use a custom domain with your Klutch.sh-deployed Aurelia app:
1. Add the Domain in Klutch.sh
In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., app.example.com).
2. Update Your DNS Provider
Update your DNS records with the CNAME provided by Klutch.sh:
CNAME: app.example.com → example-app.klutch.sh3. Wait for DNS Propagation
DNS changes can take up to 48 hours to propagate. Use tools to verify:
nslookup app.example.com# ordig app.example.comOnce propagated, your Aurelia app will be accessible at your custom domain with automatic HTTPS.
Troubleshooting
Issue 1: Build Fails with Memory Error
Error: npm run build fails with “JavaScript heap out of memory”
Solutions:
- Increase Node memory:
NODE_OPTIONS=--max-old-space-size=4096 npm run build - Set
BUILD_COMMANDin Klutch.sh:NODE_OPTIONS=--max-old-space-size=4096 npm run build - Optimize your Aurelia code and reduce bundle size
- Review large dependencies and consider alternatives
Issue 2: Port Already in Use
Error: EADDRINUSE: address already in use :::8080
Solutions:
- Ensure the internal port in Klutch.sh matches your configuration
- Kill the process using the port:
lsof -ti:8080 | xargs kill -9 - Change the port in your Dockerfile or Aurelia configuration
Issue 3: Environment Variables Not Loading
Error: Environment variables are undefined in your app
Solutions:
- Ensure variables are prefixed with
VITE_for browser access - Check that environment variables are set in the Klutch.sh dashboard
- Rebuild and redeploy after adding environment variables
- Verify variable names match exactly (case-sensitive)
Issue 4: Assets Return 404
Error: CSS, images, or JavaScript not loading in production
Solutions:
- Verify base URL is correct in your
aurelia.jsonconfiguration - Ensure all assets are in the
public/orsrc/folders - Check that asset paths are referenced correctly
- Clear browser cache and redeploy
Issue 5: Slow Build Times
Error: Build takes longer than expected
Solutions:
- Check for large dependencies or unnecessary imports
- Use lazy loading for heavy modules
- Optimize images before including them
- Set
BUILD_COMMANDif using custom build steps
Best Practices
1. Use Value Converters
Create reusable value converters for data transformation:
export class DateFormatValueConverter { toView(value: Date | string, format: string = "short"): string { const date = new Date(value); if (format === "short") { return date.toLocaleDateString(); } return date.toLocaleString(); }}2. Implement Binding Behaviors
Use binding behaviors for advanced control:
export class DebounceBindingBehavior { bind(binding: Binding, source: any, delay: number = 300) { let timeout; const originalUpdateTarget = binding.updateTarget;
binding.updateTarget = function(value) { clearTimeout(timeout); timeout = setTimeout(() => { originalUpdateTarget.call(binding, value); }, delay); }; }}3. Use Composition for Component Reuse
Compose complex UIs from simple, reusable components:
<template> <compose view-model.bind="router.currentInstruction.config.moduleId"> </compose></template>4. Implement Proper Routing
Set up clean, semantic routing:
export class App { router: Router;
configureRouter(config: RouterConfiguration, router: Router) { config.title = "Aurelia App"; config.map([ { route: ["", "home"], name: "home", component: Home, title: "Home" }, { route: "articles", name: "articles", component: Articles, title: "Articles" }, { route: "articles/:id", name: "article-detail", component: ArticleDetail, title: "Article" }, { route: "about", name: "about", component: About, title: "About" }, ]);
this.router = router; }}5. Use Dependency Injection
Leverage Aurelia’s powerful DI container:
@injection(HttpClient, AuthService, LoggerService)export class MyComponent { constructor( private http: HttpClient, private auth: AuthService, private logger: LoggerService ) {}}6. Optimize Performance with Lazy Loading
Load routes and modules on demand:
config.map([ { route: "admin", name: "admin", moduleId: "admin/index", nav: true, title: "Admin", settings: { roles: ["admin"] }, },]);7. Implement Proper Error Boundaries
Handle errors gracefully throughout your app:
export class ErrorBoundary { error: Error | null = null;
bind(bindingContext: any) { this.error = null; }
@bound catch(error: Error) { this.error = error; return true; // Prevents error propagation }}8. Keep Dependencies Updated
Regularly update Aurelia and related packages:
npm update aurelia frameworknpm audit fix9. Test Your Components
Write tests for your Aurelia components:
describe("ArticleListComponent", () => { let component: ArticleListComponent;
beforeEach(() => { const mockService = { getArticles: () => Promise.resolve([]), }; component = new ArticleListComponent(mockService as any); });
it("should load articles on attach", async () => { await component.attached(); expect(component.loading).toBe(false); });});10. Document Your Code
Maintain clear documentation for components and services:
/** * ArticleService - Handles all article-related API calls * * Methods: * - getArticles(): Retrieves all articles * - getArticle(id): Retrieves a single article by ID * - createArticle(data): Creates a new article * - updateArticle(id, data): Updates an existing article * - deleteArticle(id): Deletes an article */@injection(HttpClient)export class ArticleService { // ...}Verifying Your Deployment
After deployment completes:
- Check the App URL: Visit your app at
https://example-app.klutch.shor your custom domain. - Verify Pages Render: Ensure all pages load and components render correctly.
- Check Performance: Use Google PageSpeed Insights to verify performance.
- Test API Integration: If your app calls APIs, verify the requests work correctly.
- Review Console: Open the browser console (F12) and verify no errors are present.
- Check Network: Use browser DevTools to verify all assets load correctly.
- Review Logs: Check the Klutch.sh dashboard logs for any runtime errors.
If your app doesn’t work as expected, review the troubleshooting section and check the logs in the Klutch.sh dashboard.
External Resources
- Official Aurelia Documentation
- Aurelia Quick Start Guide
- Aurelia Component Guide
- Klutch.sh Official Website
- Node.js Documentation
- Web Vitals Guide
- Web Security Documentation
Deploying an Aurelia application to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for fine-grained control. By following this guide, you’ve learned how to scaffold an Aurelia project, create components and services, configure environment variables, implement security best practices, set up monitoring, and troubleshoot common issues. Your Aurelia application is now running on Klutch.sh’s global infrastructure with automatic HTTPS, custom domain support, and production-grade performance. For additional help or questions, consult the official Aurelia documentation or contact Klutch.sh support.