├── .gitignore
├── LICENSE
├── README.md
├── docs
├── README.md
├── cli-reference.md
├── configuration.md
├── credits.md
├── deployment.md
├── development.md
├── examples
│ ├── ai-streaming.md
│ ├── apollo-graphql.md
│ ├── astro-static.md
│ ├── deno.md
│ ├── express.md
│ ├── hono.md
│ ├── nextjs.md
│ ├── react-router-v7.md
│ └── react.md
├── faq.md
├── getting-started.md
└── research.md
├── example-ai-streaming
├── README.md
├── serverless.containers.yml
└── service
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── __tests__
│ └── ai.test.js
│ ├── index.js
│ ├── middleware
│ └── errorHandler.js
│ ├── public
│ ├── css
│ │ └── styles.css
│ ├── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
│ ├── index.html
│ └── js
│ │ └── main.js
│ ├── routes
│ └── ai.js
│ └── utils
│ └── errors.js
├── example-apollo-graphql
├── README.md
├── serverless.containers.yml
└── service
│ ├── Dockerfile
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ └── index.js
├── example-astro-static
├── README.md
├── serverless.containers.yml
└── service
│ ├── .astro
│ ├── content-assets.mjs
│ ├── content-modules.mjs
│ ├── content.d.ts
│ ├── data-store.json
│ ├── settings.json
│ └── types.d.ts
│ ├── astro.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── css
│ │ └── styles.css
│ └── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
│ └── src
│ └── pages
│ ├── health.ts
│ └── index.astro
├── example-deno
├── README.md
├── serverless.containers.yml
└── service
│ ├── Dockerfile
│ ├── deno.json
│ ├── deno.lock
│ └── src
│ ├── index.ts
│ └── public
│ ├── css
│ └── styles.css
│ └── images
│ ├── background.png
│ ├── favicon.png
│ └── logo.png
├── example-express
├── README.md
├── serverless.containers.yml
└── service
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── index.js
│ └── public
│ ├── css
│ └── styles.css
│ └── images
│ ├── background.png
│ ├── favicon.png
│ └── logo.png
├── example-hono
├── README.md
├── serverless.containers.yml
└── service
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── index.js
│ └── public
│ ├── css
│ └── styles.css
│ └── images
│ ├── background.png
│ ├── favicon.png
│ └── logo.png
├── example-mastra-slack
├── .env-example
├── README.md
├── package-lock.json
├── package.json
├── serverless.containers.yml
├── src
│ └── mastra
│ │ ├── agents
│ │ └── weather-agent.ts
│ │ ├── index.ts
│ │ ├── store.ts
│ │ └── tools
│ │ └── weather-tool.ts
└── tsconfig.json
├── example-nextjs
├── README.md
├── serverless.containers.yml
└── service
│ ├── .gitignore
│ ├── eslint.config.mjs
│ ├── next.config.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ └── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
│ ├── src
│ └── app
│ │ ├── about
│ │ └── page.tsx
│ │ ├── components
│ │ └── NavBar.tsx
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ └── tsconfig.json
├── example-react-router-v7
├── README.md
├── serverless.containers.yml
└── service
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── app
│ ├── app.css
│ ├── root.tsx
│ ├── routes.ts
│ ├── routes
│ │ ├── about.tsx
│ │ ├── health.tsx
│ │ └── home.tsx
│ └── welcome
│ │ ├── logo-dark.svg
│ │ ├── logo-light.svg
│ │ └── welcome.tsx
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ └── favicon.ico
│ ├── react-router.config.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── example-react
├── README.md
├── serverless.containers.yml
└── service
├── dev.js
├── esbuild.config.js
├── package-lock.json
├── package.json
├── public
├── css
│ └── styles.css
└── images
│ ├── background.png
│ ├── favicon.png
│ └── logo.png
├── server.js
└── src
├── About.jsx
├── App.jsx
├── Home.jsx
└── index.jsx
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | **/.vscode
3 | **/.env
4 | **/.env.*
5 | **/node_modules
6 | **/.serverless
7 | **/.next
8 | **/dist/
9 | **/build/
10 | **/releases/
11 | **/release-builds/
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2025] [Serverless, Inc.]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Container Framework - Documentation
3 | short_title: Serverless Container Framework
4 | description: >-
5 | Develop and deploy containers seamlessly across AWS Lambda and AWS ECS
6 | Fargate. Develop containerized applications with hot reloading, local
7 | emulation, and automated infrastructure setup. The perfect bridge between
8 | serverless and containers.
9 | keywords:
10 | - Serverless Container Framework
11 | - AWS Fargate
12 | - AWS Lambda
13 | - AWS ECS
14 | - Container Deployment
15 | - Serverless Containers
16 | - Docker Development
17 | - Application Load Balancer
18 | - Container Orchestration
19 | - Serverless Architecture
20 | - DevOps Automation
21 | - Infrastructure as Code
22 | - Cloud Native Development
23 | - Microservices Architecture
24 | - Local Container Development
25 | - Hot Reloading
26 | ---
27 |
28 | 
29 |
30 | # Serverless Container Framework
31 |
32 | **One solution to deploy serverless workloads everywhere** - Serverless Container Framework (SCF) is a unified development and deployment experience for containers on serverless platforms.
33 |
34 | The value of SCF lies in its Heroku-like experience for deploying containers on serverless platforms, without vendor lock-in. Deploy to any container-supporting serverless platform. While PaaS providers charge hefty markups based on compute usage, SCF charges only a fixed cost per each container you deploy. Even better, SCF's license is free for every developer/organization making less than $2 million in revenue per year.
35 |
36 | In this initial release, SCF focuses on delivering an API architecture that leverages AWS Application Load Balancer for request routing, allowing developers to freely mix and transition between AWS Lambda and AWS ECS Fargate compute options, accompanied by a rich development experience.
37 |
38 | * [Examples](https://github.com/serverless/containers)
39 | * [Overview Video (90 seconds)](https://youtu.be/KXNYemGzda4)
40 | * [Feedback Form](https://form.typeform.com/to/iqaERaLP)
41 |
42 | ::youtube{id="KXNYemGzda4"}
43 |
44 | ## Features
45 |
46 | ### Unified Container Development & Deployment
47 | - Deploy seamlessly to AWS Lambda and ECS Fargate via a single workflow (and more providers soon)
48 | - Mix Lambda and Fargate compute within a single API
49 | - Switch compute platforms instantly without code rewrites or downtime
50 | - Optimize container builds automatically for each compute service
51 | - Write Node.js and Python apps naturally - No Dockerfiles needed
52 | - Get production-ready infrastructure in seconds with automated VPC, networking & ALB setup
53 |
54 | ### Rich Development Experience
55 | - Develop Lambda and Fargate containers rapidly with true local emulation
56 | - Route and simulate AWS ALB requests via localhost
57 | - Accelerate development with instant hot reloading
58 | - Inject live AWS IAM roles into your containers
59 | - Enjoy an elegant logging and debugging experience
60 |
61 | ### Production-Ready Features
62 | - Smart code/config change detection for deployments
63 | - Supports one or multiple custom domains on the same API
64 | - Automatic SSL certificate management
65 | - Secure AWS IAM and network defaults
66 | - Load environment variables from .env, AWS Secrets Manager, AWS Systems Manager Parameter Store, HashiCorp Vault, HashiCorp Terraform state, and more via [Serverless Framework Variables](https://www.serverless.com/framework/docs/guides/variables)
67 | - Multi-cloud support coming soon
68 |
69 | # Configuration
70 |
71 | Serverless Container Framework offers simple YAML to deliver complex architectures via a `serverless.containers.yml` file. Here is a simple example of a full-stack application.
72 |
73 | ```yaml
74 | name: acmeinc
75 |
76 | deployment:
77 | type: awsApi@1.0
78 |
79 | containers:
80 | # Web (Frontend)
81 | service-web:
82 | src: ./web
83 | routing:
84 | domain: acmeinc.com
85 | pathPattern: /*
86 | compute:
87 | type: awsLambda
88 | # API (Backend)
89 | service-api:
90 | src: ./api
91 | routing:
92 | domain: api.acmeinc.com
93 | pathPattern: /api/*
94 | pathHealthCheck: /health
95 | compute:
96 | type: awsFargateEcs
97 | awsFargateEcs:
98 | memory: 4096
99 | cpu: 1024
100 | environment:
101 | HELLO: world
102 | awsIam:
103 | customPolicy:
104 | Version: "2012-10-17"
105 | Statement:
106 | - Effect: Allow
107 | Action:
108 | - dynamodb:GetItem
109 | Resource:
110 | - "*"
111 | ```
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/docs/cli-reference.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Container Framework - CLI Reference
3 | short_title: CLI Reference
4 | description: >-
5 | Comprehensive guide to Serverless Container Framework CLI commands. Learn how to deploy,
6 | develop, and manage containerized applications using SCF's powerful command line interface
7 | with detailed examples and options.
8 | keywords:
9 | - Serverless Container Framework
10 | - Serverless Container Framework CLI
11 | - Container Deployment Commands
12 | - Serverless Container Development Commands
13 | - AWS Container Management
14 | - AWS Lambda
15 | - AWS ECS
16 | - AWS Fargate
17 | - Serverless Development Tools
18 | - Container CLI Tools
19 | - DevOps Commands
20 | - Infrastructure Management
21 | - AWS Deployment Tools
22 | - Container Orchestration CLI
23 | - Serverless Framework Commands
24 | - Development Workflow
25 | - Container Management Tools
26 | ---
27 |
28 | # CLI Reference
29 |
30 | The Serverless Container Framework (SCF) provides several CLI commands to manage your containerized applications. Here's a comprehensive guide to all available commands.
31 |
32 | ## Commands
33 |
34 | ### `deploy`
35 |
36 | Deploy your containers and foundational infrastructure:
37 |
38 | ```bash
39 | serverless deploy
40 | ```
41 |
42 | #### `--force`
43 |
44 | Disregard config/code change detection and deploy all containers and infrastructure. By default, SCF uses intelligent change detection to perform incremental deployments.
45 |
46 | ### `dev`
47 |
48 | Start a local development environment with hot-reloading and local AWS emulation:
49 |
50 | ```bash
51 | serverless dev
52 | ```
53 |
54 | ### `info`
55 |
56 | Display information about your deployed container services:
57 |
58 | ```bash
59 | serverless info
60 | ```
61 |
62 | ### `remove`
63 |
64 | Remove deployed container services:
65 |
66 | ```bash
67 | serverless remove
68 | ```
69 |
70 | #### `--all`
71 |
72 | Remove all resources including shared infrastructure (e.g. VPC, ALB, ECS Cluster, etc.). Be careful when using this if some resources were created by other projects.
73 |
74 | #### `--force`
75 |
76 | Force removal of all resources including shared infrastructure without confirmation. Useful for CI/CD pipelines.
77 |
78 | ## Global Options
79 |
80 | These options can be used with any command:
81 |
82 | ### `--stage`
83 |
84 | Specify the stage to target (e.g., dev, staging, prod). Defaults to `dev` if not specified.
85 |
86 | ### `--debug`
87 |
88 | Enable detailed debug logging. Useful for troubleshooting issues.
89 |
90 | ### `--aws-profile`
91 |
92 | Specify which AWS credentials profile to use. This overrides the default AWS profile.
93 |
--------------------------------------------------------------------------------
/docs/credits.md:
--------------------------------------------------------------------------------
1 | Serverless Container Framework was built by Tomasz Czubocha, Max Marze, Austen Collins, and the rest of the team at Serverless Inc.
--------------------------------------------------------------------------------
/docs/development.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Container Framework - Development Guide
3 | short_title: Development
4 | description: >-
5 | Master local development with Serverless Container Framework. Learn about
6 | emulating AWS Lambda and ECS Fargate locally with hot-reloading, AWS IAM role
7 | testing, AWS Application Load Balancer (ALB) routing, and more.
8 | keywords:
9 | - Serverless Container Framework
10 | - Serverless Container Framework Development
11 | - Serverless Container Framework Local Development
12 | - Serverless Container Framework Hot Reloading
13 | - Serverless Container Framework Container Development
14 | - Serverless Container Framework AWS IAM Testing
15 | - Serverless Container Framework Development Mode
16 | - Serverless Container Framework Node.js Development
17 | - Python Development
18 | - Docker Development
19 | - Development Tools
20 | - Local Testing
21 | - Container Testing
22 | - DevOps Tools
23 | - Development Workflow
24 | - Local Containers
25 | - Development Environment
26 | ---
27 |
28 | # Development
29 |
30 | When developing applications with the Serverless Container Framework (SCF), you can take advantage of the Dev Mode feature which provides a local development environment that closely mirrors your production setup. It provides the following benefits:
31 |
32 | - Develop AWS Lambda and AWS Fargate containers rapidly with true local emulation
33 | - Route and simulate AWS ALB requests via `localhost`
34 | - Accelerate development with instant hot reloading
35 | - Inject live AWS IAM roles into your containers
36 | - Enjoy an elegant logging and debugging experience
37 |
38 | ## Command
39 |
40 | To start Dev Mode for your SCF project:
41 |
42 | ```bash
43 | serverless dev
44 | ```
45 |
46 | ### `--stage`
47 |
48 | By default, the project will deploy all containers in the `dev` stage. You can target a different stage by using the `--stage` flag: `serverless dev --stage prod`.
49 |
50 | ### `--debug`
51 |
52 | This flag will enable debug logging. If you encounter an issue, please enable debug logging and provide the logs to the Serverless team.
53 |
54 | ### `--port-proxy`
55 |
56 | This option will set up the local proxy that emulates the AWS Application Load Balancer (ALB) routing to use a specific port. Otherwise, the default port is `3000`.
57 |
58 | ### `--port-control`
59 |
60 | This option will set up the control plan that runs your containers locally to use a specific port. Otherwise, the default port is `3001`.
61 |
62 | ## Container Hot-Reloading
63 |
64 | Dev Mode supports hot-reloading for:
65 |
66 | - Node.js applications
67 | - Python applications
68 |
69 | When you make code changes, the containers automatically rebuild and restart while maintaining your application state.
70 |
71 | ### Node.js Hot-Reloading
72 |
73 | For Node.js applications, Hot Module Reloading (HMR) is enabled by default. Your application will automatically restart when files change.
74 |
75 | By default, SCF uses a file-watching tool (chokidar) to monitor changes in the source code.
76 |
77 | However, the container’s entrypoint script first checks for a "dev" command in the project's package.json and, when found, runs it to start the development process instead of the default HMR. If you want to customize your HMR or you are working with a framework that offers its own HMR, you can do so by adding a "dev" command to your project's package.json.
78 |
79 | ### Python Hot-Reloading
80 |
81 | Python applications use watchdog to monitor file changes and trigger rebuilds automatically.
82 |
83 | ### Custom Dockerfiles
84 |
85 | When using custom Dockerfiles, Dev Mode will still watch for file changes and trigger rebuilds.
86 |
87 | ## AWS IAM Role Testing
88 |
89 | When developing locally, SCF enables testing with live AWS IAM roles, if they are configured for a container in `containers` and the architecture has been deployed to AWS in that stage.
90 |
91 | For clarity, if you have deployed into a `dev` stage, and you run Dev Mode locally within that `dev` stage, the AWS IAM Roles for your project within that stage will be located in the live AWS account, temporary credentials for them will be created, and those credentials will be injected into your containers, allowing you to test exact IAM permissions that will be used in production.
92 |
93 | If you have not deployed your stage to AWS, no AWS IAM Roles will be available and therefore no temporary credentials will be created.
94 |
95 | This provides several key benefits:
96 |
97 | - Test exact IAM permissions that will be used in production
98 | - Catch permission issues before deployment
99 | - Use and validate AWS service integrations locally
100 |
101 | For example, if your container needs to access AWS S3 buckets or AWS DynamoDB tables, you can test these permissions locally using the actual IAM role that will be used in production.
102 |
103 | **IMPORTANT:** Local development requires a specific AWS IAM trust policy to be injected into IAM roles. This trust policy allows the local development environment to assume the role. If you have already deployed your architecture to AWS, the `dev` command will automatically inject the trust policy into the IAM roles for your containers. **You'll want to ensure that you do not do this in your production environment.**
104 |
105 |
--------------------------------------------------------------------------------
/docs/examples/express.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Express Application
3 | short_title: Serverless Express Application
4 | description: >-
5 | Deploy an Express-based web application with Serverless Container Framework.
6 | This example demonstrates how to build a simple Express application that can be deployed to
7 | AWS Lambda or AWS ECS Fargate without rearchitecting.
8 | keywords:
9 | - Serverless Container Framework
10 | - Express Framework
11 | - Web Application
12 | - Static File Serving
13 | - Health Check Endpoint
14 | - AWS Lambda Deployment
15 | - AWS ECS Fargate Deployment
16 | - Serverless Express
17 | - Container Deployment
18 | - Serverless Deployment
19 | - AWS Application Load Balancer
20 | - Local Development
21 | - Hot Reloading
22 | - Cloud Infrastructure
23 | - Node.js Application
24 | ---
25 |
26 | # Serverless Express Application
27 |
28 | This example demonstrates how to build and deploy a simple Express-based web application using the Serverless Container Framework (SCF). The application sets up basic routes—including a health check, static file delivery, and a fallback 404 page—with minimal configuration. The Serverless Container Framework enables this application to be deployed to either AWS Lambda or AWS ECS Fargate without rearchitecting.
29 |
30 | ## Features
31 |
32 | - **Express Framework:**
33 | Leverages Express for routing and middleware handling.
34 | - **Static File Serving:**
35 | Serves static assets from a dedicated public directory.
36 | - **Health Check Endpoint:**
37 | Provides a simple `/health` route for monitoring application health.
38 | - **Flexible Compute Options:**
39 | Easily switch between AWS Lambda and AWS Fargate ECS deployments via SCF configuration.
40 |
41 | ## Prerequisites
42 |
43 | Before getting started, make sure you have:
44 |
45 | - **Docker:** Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
46 | - **Serverless Framework:** Install globally:
47 | ```bash
48 | npm i -g serverless
49 | ```
50 | - **Node.js & npm:** Ensure you have a recent Node.js LTS version installed.
51 | - **AWS Credentials:** Properly configure your AWS credentials (via environment variables or AWS profiles) to allow SCF to provision and update AWS resources.
52 |
53 | For more information on setting up AWS credentials, see the [SCF Getting Started guide](../getting-started.md).
54 |
55 | ## Configuration
56 |
57 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
58 |
59 | ```yaml
60 | name: express
61 |
62 | deployment:
63 | type: awsApi@1.0
64 |
65 | containers:
66 | service:
67 | src: ./service
68 | routing:
69 | pathPattern: /*
70 | pathHealthCheck: /health
71 | environment:
72 | HELLO: world
73 | compute:
74 | type: awsLambda # or awsFargateEcs
75 | ```
76 |
77 | This configuration sets:
78 | - **Project Namespace:** The project name (express) is used as a namespace in your AWS account.
79 | - **Deployment Settings:** Configures networking (ALB, VPC, API Gateway) via the AWS API deployment type.
80 | - **Container Details:**
81 | - The source code is located in the `./service` directory.
82 | - A catch-all routing rule (`/*`) is used with a dedicated health check endpoint (`/health`).
83 | - An environment variable (`HELLO`) is provided.
84 | - The compute type is set to `awsLambda` by default (switchable to `awsFargateEcs` as needed).
85 |
86 | For more details on SCF configuration options, see the [SCF Configuration documentation](../configuration.md).
87 |
88 | ## Project Structure
89 |
90 | A typical project structure for this Express example:
91 | ```
92 | example-express/
93 | ├── serverless.containers.yml # SCF configuration file
94 | └── service/
95 | ├── package.json # Node.js project configuration and dependencies
96 | └── src/
97 | ├── index.js # Main Express application entrypoint
98 | └── public/ # Static assets (HTML, CSS, images, etc.)
99 | ```
100 |
101 | ## Development
102 |
103 | Serverless Container Framework provides a local development mode that emulates AWS routing and compute environments, including AWS Application Load Balancer emulation:
104 | ```bash
105 | serverless dev
106 | ```
107 |
108 | This will automatically start everything and set up hot reloading.
109 |
110 | For more information on local development with SCF, see the [SCF Development documentation](../development.md).
111 |
112 | ## Deployment
113 |
114 | Deploy your Express application to AWS by running:
115 | ```bash
116 | serverless deploy
117 | ```
118 |
119 | During deployment, SCF builds the container image (using the provided multi-stage Dockerfile) and provisions the necessary AWS resources (ALB, VPC, Lambda function or ECS Fargate service).
120 |
121 | For more details on deployment options and processes, see the [SCF Deployment documentation](../deployment.md).
122 |
123 | ## Integrating with other resources
124 |
125 | Serverless Container Framework supports the Serverless Framework Variables system to reference infrastructure details, secrets, and more from various sources:
126 |
127 | ```yaml
128 | containers:
129 | service:
130 | environment:
131 | # Simple static value
132 | SERVICE_NAME: express-service
133 |
134 | # Environment variable reference
135 | NODE_ENV: ${env:NODE_ENV}
136 |
137 | # AWS Systems Manager Parameter Store reference
138 | DATABASE_URL: ${aws:ssm:/path/to/database/url}
139 |
140 | # AWS Secrets Manager reference
141 | DATABASE_PASSWORD: ${aws:secretsmanager:ExpressDbSecret.password}
142 |
143 | # HashiCorp Vault reference
144 | API_SECRET: ${vault:secret/data/api/credentials.secret}
145 |
146 | # HashiCorp Terraform state reference
147 | REDIS_ENDPOINT: ${terraform:outputs:redis_endpoint}
148 |
149 | # S3 bucket value reference
150 | CONFIG_JSON: ${aws:s3:config-bucket/express-config.json}
151 |
152 | # CloudFormation stack output reference
153 | VPC_ID: ${aws:cf:networking-stack.VpcIdOutput}
154 | ```
155 |
156 | For more details on using variables, see the [Serverless Framework Variables documentation](https://www.serverless.com/framework/docs/guides/variables).
157 |
158 | ## Cleanup
159 |
160 | To remove deployed AWS resources when they are no longer needed:
161 | ```bash
162 | serverless remove --force --all
163 | ```
164 |
165 | ## Additional Resources
166 |
167 | - [Serverless Container Framework Documentation](../README.md)
168 | - [Express Documentation](https://expressjs.com)
169 | - [Docker Documentation](https://docs.docker.com)
170 | - [AWS Lambda Documentation](https://aws.amazon.com/lambda)
171 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
172 |
--------------------------------------------------------------------------------
/docs/examples/nextjs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Next.js Application
3 | short_title: Serverless Next.js Application
4 | description: >-
5 | Deploy a Next.js web application with Serverless Container Framework.
6 | This example demonstrates how to build a server-side rendered application that can be deployed to
7 | AWS Lambda or AWS ECS Fargate without rearchitecting.
8 | keywords:
9 | - Serverless Container Framework
10 | - Next.js Framework
11 | - Server-Side Rendering
12 | - Dynamic Routing
13 | - Static Site Generation
14 | - AWS Lambda Deployment
15 | - AWS ECS Fargate Deployment
16 | - Serverless Next.js
17 | - Container Deployment
18 | - Serverless Deployment
19 | - AWS Application Load Balancer
20 | - Local Development
21 | - Hot Reloading
22 | - Cloud Infrastructure
23 | - Docker Multi-stage Builds
24 | ---
25 |
26 | # Serverless Next.js Application
27 |
28 | This example demonstrates how to build and deploy a Next.js web application using the Serverless Container Framework (SCF). Due to the potential for large SSR outputs, this project is configured for deployment on AWS Fargate ECS, though the Serverless Container Framework enables this application to be deployed to either AWS Lambda or AWS ECS Fargate without rearchitecting.
29 |
30 | ## Features
31 |
32 | - **Next.js Framework:**
33 | Leverages Next.js for server-side rendering (SSR) and static site generation.
34 | - **Dynamic Routing & SSR:**
35 | Provides robust routing and dynamic page generation.
36 | - **Optimized Production Builds:**
37 | Docker multi-stage builds ensure efficient deployments.
38 | - **Flexible Compute Options:**
39 | Configured for AWS Fargate ECS to handle large HTML responses, but can be switched to AWS Lambda if response sizes are manageable.
40 |
41 | ## Prerequisites
42 |
43 | Before getting started, make sure you have:
44 |
45 | - **Docker:** Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
46 | - **Serverless Framework:** Install globally:
47 | ```bash
48 | npm i -g serverless
49 | ```
50 | - **Node.js & npm:** Ensure you have a recent Node.js LTS version installed.
51 | - **AWS Credentials:** Configure your AWS credentials (via environment variables or profiles) for SCF deployments.
52 |
53 | For more information on setting up AWS credentials, see the [SCF Getting Started guide](../getting-started.md).
54 |
55 | ## Configuration
56 |
57 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
58 |
59 | ```yaml
60 | name: nextjs
61 |
62 | deployment:
63 | type: awsApi@1.0
64 |
65 | containers:
66 | service:
67 | src: ./service
68 | routing:
69 | pathPattern: /*
70 | pathHealthCheck: /health
71 | environment:
72 | HELLO: world
73 | compute:
74 | # awsLambda is not recommended for this Next.js app.
75 | # SSR can generate large HTML responses, which may exceed
76 | # the req/res size limits for AWS Lambda.
77 | type: awsFargateEcs
78 | ```
79 |
80 | For more details on SCF configuration options, see the [SCF Configuration documentation](../configuration.md).
81 |
82 | ## Project Structure
83 |
84 | A typical project structure for this Next.js example:
85 | ```
86 | example-nextjs/
87 | ├── serverless.containers.yml # SCF configuration file
88 | └── service/
89 | ├── next.config.ts # Next.js configuration file
90 | ├── package.json # Project configuration and dependencies
91 | ├── public/ # Static assets (images, CSS, etc.)
92 | └── src/
93 | ├── app/ # Next.js app folder (pages, components, etc.)
94 | └── (other directories) # Additional assets and logic
95 | ```
96 |
97 | ## Development
98 |
99 | Serverless Container Framework provides a local development mode that emulates AWS routing and compute environments, including AWS Application Load Balancer emulation:
100 | ```bash
101 | serverless dev
102 | ```
103 | This will automatically start the Next.js development server with hot reloading and AWS emulation. It detects the dev npm script and uses that for hot reloading.
104 |
105 | For more information on local development with SCF, see the [SCF Development documentation](../development.md).
106 |
107 | ## Deployment
108 |
109 | Deploy your Next.js application to AWS by running:
110 | ```bash
111 | serverless deploy
112 | ```
113 | SCF builds the container image (using the provided multi-stage Dockerfile) and provisions the necessary AWS resources.
114 |
115 | For more details on deployment options and processes, see the [SCF Deployment documentation](../deployment.md).
116 |
117 | ## Integrating with other resources
118 |
119 | Serverless Container Framework supports the Serverless Framework Variables system to reference infrastructure details, secrets, and more from various sources:
120 |
121 | ```yaml
122 | containers:
123 | service:
124 | environment:
125 | # Simple static value
126 | SERVICE_NAME: nextjs-application
127 |
128 | # Environment variable reference
129 | NODE_ENV: ${env:NODE_ENV}
130 |
131 | # AWS Systems Manager Parameter Store reference
132 | API_ENDPOINT: ${aws:ssm:/path/to/api/endpoint}
133 |
134 | # AWS Secrets Manager reference
135 | DATABASE_PASSWORD: ${aws:secretsmanager:NextjsDbSecret.password}
136 |
137 | # HashiCorp Vault reference
138 | AUTH_SECRET: ${vault:secret/data/auth/credentials.secret}
139 |
140 | # HashiCorp Terraform state reference
141 | REDIS_ENDPOINT: ${terraform:outputs:redis_endpoint}
142 |
143 | # S3 bucket value reference
144 | CONFIG_JSON: ${aws:s3:config-bucket/nextjs-config.json}
145 |
146 | # CloudFormation stack output reference
147 | VPC_ID: ${aws:cf:networking-stack.VpcIdOutput}
148 | ```
149 |
150 | For more details on using variables, see the [Serverless Framework Variables documentation](https://www.serverless.com/framework/docs/guides/variables).
151 |
152 | ## Cleanup
153 |
154 | To remove the deployed AWS resources, run:
155 | ```bash
156 | serverless remove --force --all
157 | ```
158 |
159 | ## Additional Resources
160 |
161 | - [Serverless Container Framework Documentation](../README.md)
162 | - [Next.js Documentation](https://nextjs.org/docs)
163 | - [Docker Documentation](https://docs.docker.com)
164 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
165 |
--------------------------------------------------------------------------------
/docs/examples/react.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless React Application
3 | short_title: Serverless React Application
4 | description: >-
5 | Deploy a React application with Serverless Container Framework.
6 | This example demonstrates how to build a client-side React application that can be deployed to
7 | AWS Lambda or AWS ECS Fargate without rearchitecting.
8 | keywords:
9 | - Serverless Container Framework
10 | - React Framework
11 | - Client-Side Application
12 | - esbuild Bundling
13 | - Static Asset Serving
14 | - AWS Lambda Deployment
15 | - AWS ECS Fargate Deployment
16 | - Serverless React
17 | - Container Deployment
18 | - Serverless Deployment
19 | - AWS Application Load Balancer
20 | - Local Development
21 | - Hot Reloading
22 | - Cloud Infrastructure
23 | - Docker Multi-stage Builds
24 | ---
25 |
26 | # Serverless React Application
27 |
28 | This example demonstrates how to build and deploy a React application using the Serverless Container Framework (SCF). The application is bundled using esbuild and optimized for production deployments. The Serverless Container Framework enables this application to be deployed to either AWS Lambda or AWS ECS Fargate without rearchitecting, though it is configured for AWS Fargate ECS to accommodate larger bundle sizes.
29 |
30 | ## Features
31 |
32 | - **React Framework:**
33 | Builds a client-side React application with a component-based architecture.
34 | - **Fast Bundling with esbuild:**
35 | Uses esbuild for rapid development builds and efficient bundling.
36 | - **Static Asset Serving:**
37 | Supports serving static assets and client-side routing.
38 | - **Flexible Compute Options:**
39 | Configured for AWS Fargate ECS to accommodate larger bundle sizes that might exceed AWS Lambda request/response limits, but can be switched to AWS Lambda for smaller applications.
40 |
41 | ## Prerequisites
42 |
43 | Before getting started, make sure you have:
44 |
45 | - **Docker:** Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
46 | - **Serverless Framework:** Install globally:
47 | ```bash
48 | npm i -g serverless
49 | ```
50 | - **Node.js & npm:** Ensure you have a recent Node.js LTS version installed.
51 | - **AWS Credentials:** Set up your AWS credentials (via environment variables or profiles) for SCF deployment.
52 |
53 | For more information on setting up AWS credentials, see the [SCF Getting Started guide](../getting-started.md).
54 |
55 | ## Configuration
56 |
57 | The SCF configuration is defined in the `serverless.containers.yml` file at the project root:
58 |
59 | ```yaml
60 | name: react
61 |
62 | deployment:
63 | type: awsApi@1.0
64 |
65 | containers:
66 | service:
67 | src: ./service
68 | routing:
69 | pathPattern: /*
70 | pathHealthCheck: /health
71 | environment:
72 | HELLO: world
73 | compute:
74 | # awsLambda is not recommended for this React app.
75 | # The bundled app may exceed AWS Lambda's request/response size limits.
76 | type: awsFargateEcs
77 | ```
78 |
79 | For more details on SCF configuration options, see the [SCF Configuration documentation](../configuration.md).
80 |
81 | ## Project Structure
82 |
83 | A typical project structure for this React example:
84 | ```
85 | example-react/
86 | ├── serverless.containers.yml # SCF configuration file
87 | └── service/
88 | ├── package.json # Project configuration and dependencies
89 | ├── public/ # Static assets (HTML, CSS, images, etc.)
90 | ├── server.js # Server entrypoint for serving the React app
91 | └── src/
92 | ├── index.jsx # React application entrypoint
93 | └── (other components) # React components and logic
94 | ```
95 |
96 | ## Development
97 |
98 | Serverless Container Framework provides a local development mode that emulates AWS routing and compute environments, including AWS Application Load Balancer emulation:
99 | ```bash
100 | serverless dev
101 | ```
102 | This will automatically start the development environment with hot reloading and AWS-like routing.
103 |
104 | For more information on local development with SCF, see the [SCF Development documentation](../development.md).
105 |
106 | ## Deployment
107 |
108 | Deploy your React application to AWS by running:
109 | ```bash
110 | serverless deploy
111 | ```
112 | SCF takes care of building the container image (using the provided Dockerfile) and provisioning the necessary resources.
113 |
114 | For more details on deployment options and processes, see the [SCF Deployment documentation](../deployment.md).
115 |
116 | ## Integrating with other resources
117 |
118 | Serverless Container Framework supports the Serverless Framework Variables system to reference infrastructure details, secrets, and more from various sources:
119 |
120 | ```yaml
121 | containers:
122 | service:
123 | environment:
124 | # Simple static value
125 | SERVICE_NAME: react-application
126 |
127 | # Environment variable reference
128 | NODE_ENV: ${env:NODE_ENV}
129 |
130 | # AWS Systems Manager Parameter Store reference
131 | API_ENDPOINT: ${aws:ssm:/path/to/api/endpoint}
132 |
133 | # AWS Secrets Manager reference
134 | API_KEY: ${aws:secretsmanager:ReactApiSecret.key}
135 |
136 | # HashiCorp Vault reference
137 | AUTH_SECRET: ${vault:secret/data/auth/credentials.secret}
138 |
139 | # HashiCorp Terraform state reference
140 | REDIS_ENDPOINT: ${terraform:outputs:redis_endpoint}
141 |
142 | # S3 bucket value reference
143 | CONFIG_JSON: ${aws:s3:config-bucket/react-config.json}
144 |
145 | # CloudFormation stack output reference
146 | VPC_ID: ${aws:cf:networking-stack.VpcIdOutput}
147 | ```
148 |
149 | For more details on using variables, see the [Serverless Framework Variables documentation](https://www.serverless.com/framework/docs/guides/variables).
150 |
151 | ## Cleanup
152 |
153 | To remove the deployed AWS resources, run:
154 | ```bash
155 | serverless remove --force --all
156 | ```
157 |
158 | ## Additional Resources
159 |
160 | - [Serverless Container Framework Documentation](../README.md)
161 | - [React Documentation](https://reactjs.org/docs/getting-started.html)
162 | - [esbuild Documentation](https://esbuild.github.io)
163 | - [Docker Documentation](https://docs.docker.com)
164 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
165 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | ## How is Serverless Container Framework different from Serverless Framework?
4 |
5 | Serverless Framework has historically focused on AWS Lambda deployment — and it is the best in class at it. If you're looking for a tool that specializes in AWS Lambda use-cases (e.g. APIs, event-driven architectures, Step Functions) and has a large ecosystem of plugins expanding those capabilities, Serverless Framework is your best choice. While it offers some container support, this isn't its core focus.
6 |
7 | Serverless Container Framework (SCF) takes a fundamentally different approach by embracing containers as the deployment artifact. This brings several key advantages:
8 |
9 | - **Cross-Provider Deployment**: Deploy to different compute services and cloud providers without being an expert in each one
10 | - **Consistent Experience**: Use the same development and deployment workflow everywhere
11 | - **No Re-architecting**: Move your containerized applications between providers without changing your code
12 | - **Container Ecosystem**: Leverage the rich container tooling and practices you already know
13 |
14 | While SCF and Serverless Framework share some common ground, SCF's focus on container portability and provider-agnostic deployments sets it on a distinct path. It maintains all the convenience of serverless while adding the flexibility and portability that containers provide.
15 |
16 | ## Why does the Serverless Container Framework need to exist?
17 |
18 | We built the Serverless Container Framework in response to a clear need from our Serverless Framework customers. Many have high-volume AWS Lambda functions they need to migrate—whether to reduce costs, improve performance, or access features that Lambda doesn't support. While AWS Lambda is excellent for prototyping and rapid scaling, it left many wondering: what comes after that? The serverless story felt incomplete.
19 |
20 | Further, we observed that serverless architectures often struggle to integrate with organizations' main workflows, largely because enterprise standards are built around containers—an area where AWS Lambda has historically fallen short.
21 |
22 | Now, with recent performance improvements in container support on AWS Lambda, we believe the time is right for a new container deployment tool that fully embraces containers as the deployment artifact and leverages the rich container ecosystem.
23 |
24 | ## How much does Serverless Container Framework cost?
25 |
26 | Serverless Container Framework follows Serverless Inc's Customer & License Agreement, making it free for developers and organizations earning less than $2 million USD in gross revenue per year. For organizations exceeding this revenue threshold, a credit-based pricing model is currently in effect, where a credit is charged for each deployed container per month.
27 |
28 | For clarity, Serverless Inc does not charge for 1) individual users, 2) usage (e.g. requests served, or compute used, like a hosting provider), 3) invidividual CLI actions (e.g. each time you run "deploy"). Instead, the credit price is a fixed price for a container that has been in a deployed state for longer than 10 days within a current month. The 10 day timeframe allows many testing/preview deployed instances to be created without any cost.
29 |
30 | ## How does Serverless Container Framework compare to other container deployment tools?
31 |
32 | There are lots of tools for deploying containers (Terraform, Pulumi, etc.), but they only give you pieces, requiring you to assemble (and maintain!) a rich deployment and development experience.
33 |
34 | While that's great for a lot of infra, for your core compute development experience, we feel developers need more.
35 |
36 | What we do at Serverless Inc is offer exceptional infrastructure-as-code experiences for critical use-cases. We go beyond basic deploy/remove operations to provide the advanced automation developers need. SCF features a rich development mode that emulates cloud compute locally, with a local API that matches AWS Application Load Balancer behavior, real-time log streaming, hot module reloading, and zero-downtime switching between AWS Lambda and ECS Fargate.
37 |
38 | ## What are the downsides of Serverless Container Framework?
39 |
40 | If you're coming from AWS Lambda, some of the downsides are:
41 |
42 | * **Longer deployment times**: Container deployments take longer than function deployments for two reasons:
43 | - Container images are larger than function packages
44 | - AWS Fargate requires additional infrastructure setup for safe deployments
45 |
46 | * **Request/Response limitations for AWS Lambda and AWS ALB**: AWS Lambda configured to AWS Application Load Balancer has a 1MB payload limit on request and response sizes (this includes headers). While compression can help, you'll need to architect your application with these limits in mind. We're actively working with AWS to advocate for higher limits, and are very optimistic that this will be addressed in the near future.
47 |
48 | * **Lack of streaming response support for AWS Lambda and AWS ALB**: AWS Lambda and AWS ALB do not support streaming responses. This means that if your application attempts to stream a response, ALB will await the whole response before sending any data to the client.
49 |
50 | If you're coming from other container deployment tools, the main consideration is maturity. As a newer solution in this space, we're continuously evolving. However, we believe our developer experience already surpasses existing tools, offering unique features like local cloud emulation, real-time logs, and seamless Lambda/ECS switching.
51 |
--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Serverless Container Framework - Getting Started
3 | short_title: Getting Started
4 | description: >-
5 | Quick start guide for Serverless Container Framework. Learn how to install,
6 | configure, and deploy your first containerized application with step-by-step
7 | instructions for AWS Lambda and AWS ECS Fargate.
8 | keywords:
9 | - Serverless Container Framework
10 | - Serverless Container Framework Getting Started
11 | - Serverless Container Framework Quick Start
12 | - Serverless Container Framework Container Setup
13 | - Serverless Container Framework AWS Setup
14 | - Serverless Container Framework Installation Guide
15 | - Serverless Container Framework First Deployment
16 | - Serverless Container Framework Container Tutorial
17 | - Serverless Container Framework AWS Configuration
18 | - Serverless Container Framework Development Setup
19 | - Serverless Container Framework Docker Setup
20 | - Serverless Container Framework Initial Configuration
21 | - Serverless Setup
22 | - Container Basics
23 | - AWS Integration
24 | ---
25 |
26 | # Getting Started
27 |
28 | This guide will help you get started with Serverless Container Framework (SCF) by deploying a simple API.
29 |
30 | ### Prerequisites
31 | - Node.js 20.x or later
32 | - AWS Account with administrative access
33 | - Docker installed and running
34 |
35 | ### Installation & Setup
36 |
37 | 1. SCF resides within the [Serverless Framework](https://github.com/serverless/serverless). Install the Serverless Framework CLI globally:
38 |
39 | ```bash
40 | npm install -g serverless
41 | ```
42 |
43 | 2. Configure your AWS credentials using one of these methods. [Additional options can be found here.](https://www.serverless.com/framework/docs/providers/aws/guide/credentials)
44 |
45 | ```bash
46 | # Option 1: AWS CLI (recommended)
47 | aws configure
48 |
49 | # Option 2: Environment variables
50 | export AWS_ACCESS_KEY_ID=your-key-id
51 | export AWS_SECRET_ACCESS_KEY=your-access-key
52 | export AWS_SESSION_TOKEN=your-session-token
53 | ```
54 |
55 | ### Getting Started
56 |
57 | 1. Start with an example project by cloning the repository:
58 | ```bash
59 | git clone https://github.com/serverless/containers.git
60 | ```
61 |
62 | 2. Navigate to the example project directory, and install any dependencies:
63 | ```bash
64 | cd example-express/service
65 | npm install
66 | ```
67 |
68 | ### Development
69 |
70 | Ensure you are within the directory containing the `serverless.containers.yml` file.
71 | ```bash
72 | cd example-express
73 | ```
74 |
75 | Start the local development environment:
76 | ```bash
77 | serverless dev
78 | ```
79 |
80 | This starts a local emulation of AWS Application Load Balancer at `http://localhost:3000`. This will forward requests to your containers. Logs, requests and more from your containers will be available in the terminal. Your containers will auto-reload or rebuild on code changes.
81 |
82 | ### Deployment
83 |
84 | Deploy to AWS:
85 | ```bash
86 | serverless deploy
87 | ```
88 |
89 | The initial deployment creates AWS resources (ALB, VPC, etc.) and takes ~5-10 minutes. Subsequent deploys are faster.
90 |
91 | ### Cleanup
92 |
93 | Remove your deployment:
94 | ```bash
95 | # Remove application only
96 | serverless remove
97 |
98 | # Remove all AWS resources including VPC
99 | serverless remove --force
100 | ```
101 |
102 | ### Troubleshooting
103 | - Ensure Docker daemon is running for local development
104 | - Check AWS credentials are properly configured using `aws sts get-caller-identity`
105 | - View detailed logs with `serverless dev --debug` or `serverless deploy --debug`
106 |
--------------------------------------------------------------------------------
/example-ai-streaming/README.md:
--------------------------------------------------------------------------------
1 | # Example Streaming AI Chat Interface
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) is a powerful tool that simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy an Express-based AI fullstack streaming application that uses Server-Sent Events (SSE) to stream live AI responses from multiple providers (OpenAI and Anthropic). It includes both a front-end and a back-end.The application leverages SCF for local development, flexible compute configurations, and smooth AWS deployments.
6 |
7 | **Updated with Claude 3.7 Sonnet Support**
8 |
9 | ## Features
10 |
11 | - **Express & SSE Streaming:**
12 | Leverages Express as the HTTP server and streams AI responses using Server-Sent Events.
13 | - **Multi-Provider Support:**
14 | Integrates with both OpenAI and Anthropic APIs to provide AI completions.
15 | - **Node.js Application:**
16 | Built with Node.js and modern JavaScript using lightweight frameworks.
17 | - **Compute Flexibility:**
18 | Easily switch between AWS Lambda and AWS Fargate ECS deployments via the SCF configuration.
19 | - **Local Development Experience:**
20 | SCF provides a rich local development mode that emulates the cloud environment, complete with hot reloading and AWS-like routing.
21 |
22 | ## Prerequisites
23 |
24 | **Docker:**
25 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
26 |
27 | **Serverless Framework:**
28 | Install the Serverless Framework globally:
29 | ```bash
30 | npm i -g serverless
31 | ```
32 |
33 | **Node.js & npm:**
34 | Ensure you have a recent Node.js LTS version installed.
35 |
36 | **AWS Credentials:**
37 | Properly configure your AWS credentials (via environment variables or AWS profiles) to allow SCF to provision and update AWS resources.
38 |
39 | ## Configuration
40 |
41 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
42 |
43 | ```yaml
44 | name: ai-streaming
45 |
46 | deployment:
47 | type: awsApi@1.0
48 |
49 | containers:
50 | service:
51 | src: ./service
52 | routing:
53 | pathPattern: /*
54 | pathHealthCheck: /health
55 | environment:
56 | OPENAI_API_KEY: ${env:OPENAI_API_KEY}
57 | ANTHROPIC_API_KEY: ${env:ANTHROPIC_API_KEY}
58 | compute:
59 | type: awsLambda # or awsFargateEcs
60 | ```
61 |
62 | This file specifies:
63 | - **Project Name:** Used as a namespace in your AWS account.
64 | - **Deployment Settings:** Configures networking (ALB, VPC) via the AWS API deployment type.
65 | - **Container Details:**
66 | - The source code is located in the `./service` directory.
67 | - A catch-all routing rule (`/*`) is used with a dedicated health check endpoint (`/health`).
68 | - API keys for OpenAI and Anthropic are injected as environment variables.
69 | - The compute type is set to `awsLambda` by default (switchable to `awsFargateEcs` as needed).
70 |
71 | ## Project Structure
72 |
73 | A typical project structure looks like this:
74 | ```
75 | example-ai-streaming/
76 | ├── serverless.containers.yml # SCF project configuration file
77 | └── service/
78 | ├── Dockerfile # Multi-stage Dockerfile for Lambda and Fargate
79 | ├── package.json # Node.js project configuration and dependencies
80 | ├── .env # Environment variables (not committed)
81 | └── src/
82 | ├── index.js # Main Express application entrypoint
83 | ├── routes/ # API route definitions (including AI streaming endpoint)
84 | ├── middleware/ # Custom middleware (error handling, etc.)
85 | └── public/ # Static assets (HTML, CSS, JS, images)
86 | ```
87 |
88 | ## Development
89 |
90 | SCF provides a local development mode that emulates AWS routing, Lambda, and ECS Fargate environments. To start the development mode (with hot reloading on file changes), run:
91 |
92 | ```bash
93 | serverless dev
94 | ```
95 |
96 | Additionally, you can run the application directly using:
97 | ```bash
98 | npm start
99 | ```
100 |
101 | ## Deployment
102 |
103 | Deploy your AI streaming application to AWS with:
104 | ```bash
105 | serverless deploy
106 | ```
107 |
108 | During deployment, SCF builds the container image (using the provided Dockerfile) and provisions AWS resources (ALB, VPC, Lambda function or ECS service) automatically.
109 |
110 | ## Cleanup
111 |
112 | To remove deployed AWS resources when they are no longer needed:
113 | ```bash
114 | serverless remove
115 | ```
116 |
117 | For complete cleanup (including shared infrastructure):
118 | ```bash
119 | serverless remove --force --all
120 | ```
121 |
122 | ## Additional Resources
123 |
124 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
125 | - [Express Documentation](https://expressjs.com)
126 | - [OpenAI API Documentation](https://platform.openai.com/docs)
127 | - [Anthropic API Documentation](https://docs.anthropic.com)
128 |
--------------------------------------------------------------------------------
/example-ai-streaming/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: ai-streaming
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | OPENAI_API_KEY: ${env:OPENAI_API_KEY}
14 | ANTHROPIC_API_KEY: ${env:ANTHROPIC_API_KEY}
15 | compute:
16 | type: awsLambda # or awsFargateEcs
--------------------------------------------------------------------------------
/example-ai-streaming/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-express-ai-streaming",
3 | "version": "1.0.0",
4 | "description": "Example of AI streaming with Express and SSE",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "node src/index.js",
8 | "test": "jest"
9 | },
10 | "dependencies": {
11 | "express": "^4.21.2",
12 | "openai": "^4.82.0",
13 | "@anthropic-ai/sdk": "^0.36.0",
14 | "dotenv": "^16.4.1",
15 | "cors": "^2.8.5"
16 | },
17 | "devDependencies": {
18 | "jest": "^29.7.0",
19 | "supertest": "^6.3.4"
20 | }
21 | }
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/__tests__/ai.test.js:
--------------------------------------------------------------------------------
1 | const request = require("supertest");
2 | const app = require("../index");
3 |
4 | describe("AI Streaming API", () => {
5 | test("OpenAI streaming endpoint returns SSE response", async () => {
6 | const response = await request(app)
7 | .post("/api/chat")
8 | .send({
9 | provider: "openai",
10 | model: "gpt-4",
11 | messages: [{ role: "user", content: "Hello" }],
12 | });
13 |
14 | expect(response.headers["content-type"]).toMatch(/text\/event-stream/);
15 | expect(response.status).toBe(200);
16 | });
17 |
18 | test("Anthropic streaming endpoint returns SSE response", async () => {
19 | const response = await request(app)
20 | .post("/api/chat")
21 | .send({
22 | provider: "anthropic",
23 | model: "claude-3-5-sonnet-20241022",
24 | messages: [{ role: "user", content: "Hello" }],
25 | });
26 |
27 | expect(response.headers["content-type"]).toMatch(/text\/event-stream/);
28 | expect(response.status).toBe(200);
29 | });
30 | });
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Main application file for the Express AI Streaming example.
3 | * Sets up middleware, routes, and error handling before starting the server.
4 | */
5 |
6 | const express = require("express");
7 | const path = require("path");
8 | const cors = require("cors");
9 | const { setupAIRoutes } = require("./routes/ai");
10 | const { errorHandler } = require("./middleware/errorHandler");
11 |
12 | const app = express();
13 | const port = process.env.PORT || 8080;
14 |
15 | /**
16 | * Configure middleware
17 | */
18 | app.use(cors());
19 | app.use(express.json());
20 | app.use(express.static(path.join(__dirname, "public")));
21 |
22 | // API Routes - these should be handled first
23 | setupAIRoutes(app);
24 |
25 | /**
26 | * Health check endpoint handler.
27 | *
28 | * @param {Object} req - Express request object.
29 | * @param {Object} res - Express response object.
30 | */
31 | app.get("/health", (req, res) => {
32 | res.status(200).json({ status: "healthy" });
33 | });
34 |
35 | /**
36 | * Serve index.html for all remaining routes
37 | * This enables client-side routing to work properly
38 | *
39 | * @param {Object} req - Express request object
40 | * @param {Object} res - Express response object
41 | */
42 | app.get("*", (req, res) => {
43 | res.sendFile(path.join(__dirname, "public", "index.html"));
44 | });
45 |
46 | // Error handling
47 | app.use(errorHandler);
48 |
49 | /**
50 | * Starts the Express server on the specified port.
51 | *
52 | * @param {Object} config - Server configuration object.
53 | * @param {number} config.port - Port number on which to run the server.
54 | */
55 | const startServer = ({ port: serverPort }) => {
56 | app.listen(serverPort, "0.0.0.0", () => {
57 | console.log(`Server running on port ${serverPort}`);
58 | });
59 | };
60 |
61 | startServer({ port });
62 |
63 | module.exports = app;
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/middleware/errorHandler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Global error handling middleware
3 | *
4 | * @param {Error} err - Error object
5 | * @param {Object} req - Express request object
6 | * @param {Object} res - Express response object
7 | * @param {Function} next - Next middleware function
8 | */
9 | function errorHandler(err, req, res, next) {
10 | console.error(err);
11 |
12 | const status = err.status || 500;
13 | const message = err.message || "Internal Server Error";
14 | const code = err.code || "internal_error";
15 |
16 | res.status(status).json({
17 | error: message,
18 | message,
19 | code,
20 | status,
21 | });
22 | }
23 |
24 | module.exports = { errorHandler };
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html, body {
8 | width: 100%;
9 | height: 100%;
10 | font-family: "Roboto Mono", monospace;
11 | /* Set a static background image */
12 | background: url('/images/background.png') no-repeat center center fixed;
13 | background-size: cover;
14 | background-color: #000000;
15 | color: #ffffff;
16 | }
17 |
18 | /**
19 | * Container for centering content. No animation is applied here,
20 | * so that the logo and chat container animate independently.
21 | */
22 | .container {
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | justify-content: center;
27 | transform: translateY(-50px);
28 | min-height: 100vh;
29 | width: 90%;
30 | max-width: 800px;
31 | margin: 0 auto;
32 | padding: 0 1rem;
33 | text-align: center;
34 | }
35 |
36 | /**
37 | * Logo element styles. The logo starts slightly scaled down (0.9) and hidden (opacity 0),
38 | * then fades in with a slight zoom effect over 0.25s, starting after a 0.75s delay.
39 | * The ease-out timing function makes the zoom effect decelerate smoothly.
40 | */
41 | .logo {
42 | max-width: 200px;
43 | width: 100%;
44 | height: auto;
45 | margin-bottom: 60px;
46 | opacity: 0;
47 | transform: scale(0.9);
48 | animation: logoFadeZoom 0.25s ease-out forwards;
49 | animation-delay: 0.75s;
50 | }
51 |
52 | /**
53 | * Chat container fades in over 0.25s, with a delay of 0.5s.
54 | */
55 | .chat-container {
56 | background: #1c1c1c;
57 | border: 1px solid #333333;
58 | border-radius: 8px;
59 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
60 | overflow: hidden;
61 | width: 100%;
62 | color: #ffffff;
63 | opacity: 0;
64 | animation: fadeIn 0.25s ease-in forwards;
65 | animation-delay: 0.5s;
66 | }
67 |
68 | .chat-output {
69 | height: 400px;
70 | padding: 1rem;
71 | overflow-y: auto;
72 | border-bottom: 1px solid #333333;
73 | }
74 |
75 | .loading {
76 | opacity: 0.3;
77 | }
78 |
79 | .input-container {
80 | padding: 1rem;
81 | }
82 |
83 | textarea {
84 | width: 100%;
85 | min-height: 100px;
86 | padding: 0.5rem;
87 | margin-bottom: 1rem;
88 | border: 1px solid #333333;
89 | border-radius: 4px;
90 | background: #1f1f1f;
91 | color: #ffffff;
92 | resize: vertical;
93 | }
94 |
95 | /**
96 | * Removes the default outline on focus for the textarea.
97 | */
98 | textarea:focus {
99 | outline: none;
100 | border-color: #555555;
101 | }
102 |
103 | .message {
104 | display: flex;
105 | align-items: flex-start;
106 | padding-bottom: 15px;
107 | }
108 |
109 | .message .sender-label {
110 | font-weight: bold;
111 | margin-right: 8px;
112 | min-width: 100px;
113 | width: 100px;
114 | text-align: left;
115 | word-wrap: break-word;
116 | }
117 |
118 | .message .message-text {
119 | flex: 1;
120 | text-align: left;
121 | padding-left: 10px;
122 | }
123 |
124 | .button-group {
125 | display: flex;
126 | width: 100%;
127 | justify-content: space-between;
128 | align-items: center;
129 | }
130 |
131 | select {
132 | padding: 0.5rem;
133 | border: 1px solid #333333;
134 | border-radius: 4px;
135 | background: #1f1f1f;
136 | color: #ffffff;
137 | }
138 |
139 | /**
140 | * Removes the default outline on focus for the dropdown.
141 | */
142 | select:focus {
143 | outline: none;
144 | border-color: #555555;
145 | }
146 |
147 | button {
148 | padding: 0.5rem 1rem;
149 | background: #fd5750;
150 | color: white;
151 | border: 1px solid #333333;
152 | border-radius: 4px;
153 | cursor: pointer;
154 | }
155 |
156 | /**
157 | * Removes the default outline on focus/active state for the button.
158 | */
159 | button:focus,
160 | button:active {
161 | outline: none;
162 | border-color: #555555;
163 | }
164 |
165 | /**
166 | * Keyframes for a simple fade-in effect.
167 | */
168 | @keyframes fadeIn {
169 | from {
170 | opacity: 0;
171 | }
172 | to {
173 | opacity: 1;
174 | }
175 | }
176 |
177 | /**
178 | * Keyframes for the logo's fade and zoom in effect.
179 | */
180 | @keyframes logoFadeZoom {
181 | from {
182 | opacity: 0;
183 | transform: scale(0.9);
184 | }
185 | to {
186 | opacity: 1;
187 | transform: scale(1);
188 | }
189 | }
190 |
191 | /* Responsive design adjustments */
192 | @media (max-width: 600px) {
193 | .chat-output {
194 | height: 300px;
195 | }
196 |
197 | .logo {
198 | max-width: 150px;
199 | margin-bottom: 30px;
200 | }
201 |
202 | textarea, button, select {
203 | font-size: 1rem;
204 | padding: 0.5rem;
205 | }
206 | }
207 | }
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-ai-streaming/service/src/public/images/background.png
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-ai-streaming/service/src/public/images/favicon.png
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-ai-streaming/service/src/public/images/logo.png
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Serverless Container Framework - AI Streaming Demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |

15 |
16 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/public/js/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Asynchronously sends a message to the specified AI provider with the selected model version and streams the response.
3 | *
4 | * @param {Object} options - Options object.
5 | * @param {string} options.provider - The AI provider (e.g., "openai" or "anthropic").
6 | * @param {string} options.model - The model version (e.g., "gpt-4", "claude-3-5-sonnet-20241022").
7 | */
8 | async function sendMessage({ provider, model }) {
9 | const input = document.getElementById('user-input');
10 | const output = document.getElementById('chat-output');
11 | const messageText = input.value.trim();
12 |
13 | if (!messageText) return;
14 |
15 | // Create user message element with sender label
16 | const userMessageDiv = document.createElement('div');
17 | userMessageDiv.className = 'message user-message';
18 |
19 | const userLabel = document.createElement('span');
20 | userLabel.className = 'sender-label';
21 | userLabel.textContent = 'User:';
22 |
23 | const userTextSpan = document.createElement('span');
24 | userTextSpan.className = 'message-text';
25 | userTextSpan.textContent = messageText;
26 |
27 | userMessageDiv.appendChild(userLabel);
28 | userMessageDiv.appendChild(userTextSpan);
29 | output.appendChild(userMessageDiv);
30 |
31 | // Clear the input field
32 | input.value = '';
33 |
34 | // Create AI message element with sender label using the provider name
35 | const aiMessageDiv = document.createElement('div');
36 | aiMessageDiv.className = 'message model-message';
37 |
38 | const aiLabel = document.createElement('span');
39 | aiLabel.className = 'sender-label';
40 | // Capitalize the provider to serve as the model name label.
41 | aiLabel.textContent = provider.charAt(0).toUpperCase() + provider.slice(1) + ':';
42 | // Apply loading class to reduce opacity while waiting
43 | aiLabel.classList.add('loading');
44 |
45 | const aiTextSpan = document.createElement('span');
46 | aiTextSpan.className = 'message-text';
47 | // Set initial loading text for AI response with reduced opacity
48 | aiTextSpan.textContent = 'Loading...';
49 | aiTextSpan.classList.add('loading');
50 |
51 | aiMessageDiv.appendChild(aiLabel);
52 | aiMessageDiv.appendChild(aiTextSpan);
53 | output.appendChild(aiMessageDiv);
54 |
55 | try {
56 | const response = await fetch(`/api/chat`, {
57 | method: 'POST',
58 | headers: {
59 | 'Content-Type': 'application/json',
60 | },
61 | body: JSON.stringify({
62 | provider,
63 | model,
64 | messages: [{ role: 'user', content: messageText }],
65 | }),
66 | });
67 |
68 | // Flag to clear the loading text/classes on the first chunk of data
69 | let firstChunk = true;
70 | const reader = response.body.getReader();
71 | const decoder = new TextDecoder();
72 |
73 | while (true) {
74 | const { value, done } = await reader.read();
75 | if (done) break;
76 |
77 | const chunk = decoder.decode(value);
78 | const lines = chunk.split('\n');
79 |
80 | for (const line of lines) {
81 | if (line.startsWith('data: ')) {
82 | const data = line.slice(6);
83 | if (data === '[DONE]') break;
84 |
85 | try {
86 | const parsed = JSON.parse(data);
87 | const { content } = parsed;
88 | if (firstChunk) {
89 | // Remove the loading class from both the sender label and text span
90 | aiLabel.classList.remove('loading');
91 | aiTextSpan.classList.remove('loading');
92 | // Clear the "Loading..." text on first chunk received
93 | aiTextSpan.textContent = '';
94 | firstChunk = false;
95 | }
96 | // Append the streamed content to the AI message text
97 | aiTextSpan.textContent += content;
98 | } catch (e) {
99 | console.error('Error parsing SSE data:', e);
100 | }
101 | }
102 | }
103 | }
104 | } catch (error) {
105 | console.error('Error:', error);
106 | aiTextSpan.textContent = 'Error: Failed to get AI response';
107 | }
108 |
109 | // Scroll to the bottom of the chat output
110 | output.scrollTop = output.scrollHeight;
111 | }
112 |
113 | /**
114 | * Triggers the send message action by extracting the selected provider and model version from the dropdown.
115 | */
116 | function triggerSendMessage() {
117 | const modelSelect = document.getElementById('model-select');
118 | // The value is expected to be in the format "provider:model"
119 | const [provider, model] = modelSelect.value.split(':');
120 | sendMessage({ provider, model });
121 | }
122 |
123 | // Add an event listener to send the message when the Enter key is pressed in the textarea
124 | document.getElementById('user-input').addEventListener('keydown', (event) => {
125 | if (event.key === 'Enter' && !event.shiftKey) {
126 | event.preventDefault();
127 | triggerSendMessage();
128 | }
129 | });
130 |
131 | // Add an event listener to the Send button
132 | document.getElementById('send-button').addEventListener('click', () => {
133 | triggerSendMessage();
134 | });
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/routes/ai.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Sets up a unified AI chat route for the Express application.
3 | * Streams AI responses using Server-Sent Events (SSE) from real providers.
4 | */
5 |
6 | const OpenAI = require("openai");
7 | const Anthropic = require("@anthropic-ai/sdk");
8 | const { AIStreamingError } = require("../utils/errors");
9 |
10 | /**
11 | * Configures the AI streaming route on the provided Express app instance.
12 | *
13 | * @param {Object} app - Express application instance.
14 | */
15 | const setupAIRoutes = (app) => {
16 | // Initialize SDK clients for OpenAI and Anthropic using API keys from environment variables.
17 | const openai = new OpenAI({
18 | apiKey: process.env.OPENAI_API_KEY,
19 | });
20 |
21 | const anthropic = new Anthropic({
22 | apiKey: process.env.ANTHROPIC_API_KEY,
23 | });
24 |
25 | /**
26 | * Handles the POST /api/chat endpoint.
27 | * Streams AI chat completions from the specified provider.
28 | * Expects a JSON payload containing provider, model, and messages.
29 | *
30 | * Example payload:
31 | * {
32 | * "provider": "openai",
33 | * "model": "gpt-4",
34 | * "messages": [{ "role": "user", "content": "Hello" }]
35 | * }
36 | *
37 | * @param {Object} req - Express request object.
38 | * @param {Object} res - Express response object.
39 | * @param {Function} next - Next middleware function.
40 | */
41 | app.post("/api/chat", async (req, res, next) => {
42 | try {
43 | const { provider, model, messages } = req.body;
44 | if (!provider || !model || !messages) {
45 | res.status(400).json({ error: "Missing required fields: provider, model, or messages" });
46 | return;
47 | }
48 |
49 | // Set up Server-Sent Events (SSE) headers.
50 | res.setHeader("Content-Type", "text/event-stream");
51 | res.setHeader("Cache-Control", "no-cache");
52 | res.setHeader("Connection", "keep-alive");
53 |
54 | let stream;
55 | if (provider === "openai") {
56 | // Stream chat completions using OpenAI.
57 | stream = await openai.chat.completions.create({
58 | model,
59 | messages,
60 | stream: true,
61 | });
62 | } else if (provider === "anthropic") {
63 | // Stream messages using Anthropic.
64 | stream = await anthropic.messages.create({
65 | model,
66 | messages,
67 | stream: true,
68 | max_tokens: 1024,
69 | });
70 | } else {
71 | res.status(400).json({ error: "Invalid provider" });
72 | return;
73 | }
74 |
75 | // Stream the AI response data back to the client.
76 | for await (const chunk of stream) {
77 | let content = "";
78 | if (provider === "openai") {
79 | content = chunk.choices[0]?.delta?.content || "";
80 | } else if (provider === "anthropic") {
81 | content = chunk.delta?.text || "";
82 | }
83 | if (content) {
84 | res.write(`data: ${JSON.stringify({ content })}\n\n`);
85 | }
86 | }
87 |
88 | // Signal end of stream.
89 | res.write("data: [DONE]\n\n");
90 | res.end();
91 | } catch (error) {
92 | next(new AIStreamingError(`${req.body.provider} streaming failed`, error));
93 | }
94 | });
95 | };
96 |
97 | module.exports = { setupAIRoutes };
--------------------------------------------------------------------------------
/example-ai-streaming/service/src/utils/errors.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom error class for AI streaming errors
3 | */
4 | class AIStreamingError extends Error {
5 | /**
6 | * Creates an AI streaming error
7 | *
8 | * @param {string} message - Error message
9 | * @param {Error} originalError - Original error object
10 | */
11 | constructor(message, originalError) {
12 | super(message);
13 | this.name = "AIStreamingError";
14 | this.status = 500;
15 | this.code = "ai_streaming_error";
16 | this.originalError = originalError;
17 | }
18 | }
19 |
20 | module.exports = { AIStreamingError };
--------------------------------------------------------------------------------
/example-apollo-graphql/README.md:
--------------------------------------------------------------------------------
1 | # Apollo GraphQL API Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) streamlines containerized application development and deployment on AWS Lambda and AWS Fargate ECS.
4 |
5 | This example demonstrates building a GraphQL API using Apollo Server with Express integration. It showcases a modern Apollo Server setup complete with a GraphQL schema, resolvers, and a health check endpoint—all deployable via SCF.
6 |
7 | ## Features
8 |
9 | - **Apollo Server & GraphQL:**
10 | Leverages Apollo Server 4 to provide a robust GraphQL API.
11 | - **Express Integration:**
12 | Uses Express middleware for seamless HTTP handling and CORS support.
13 | - **Node.js Application:**
14 | Developed with Node.js using ES module syntax.
15 | - **Flexible Compute Options:**
16 | Easily deployable as an AWS Lambda function or on AWS Fargate ECS with a simple configuration change.
17 | - **Local Development:**
18 | Utilize SCF's development mode for near-production emulation on your local machine.
19 |
20 | ## Prerequisites
21 |
22 | **Docker:**
23 | Docker Desktop is required for container builds and local development.
24 | [Get Docker](https://www.docker.com)
25 |
26 | **Serverless Framework:**
27 | Install globally:
28 | ```bash
29 | npm i -g serverless
30 | ```
31 |
32 | **Node.js & npm:**
33 | Ensure a recent Node.js LTS version is installed.
34 |
35 | **AWS Credentials:**
36 | Properly configure your AWS credentials (using environment variables or AWS profiles) to enable resource provisioning via SCF.
37 |
38 | ## Configuration
39 |
40 | The SCF configuration is defined in the `serverless.containers.yml` file at the project root:
41 |
42 | ```yaml
43 | name: apollo-graphql
44 |
45 | deployment:
46 | type: awsApi@1.0
47 |
48 | containers:
49 | service:
50 | src: ./service
51 | routing:
52 | pathPattern: /*
53 | pathHealthCheck: /health
54 | environment:
55 | NODE_ENV: production
56 | compute:
57 | type: awsLambda # Can be switched to awsFargateEcs
58 | ```
59 |
60 | This file sets the project name, deployment type, and container settings (including routing, environment variables, and compute type).
61 |
62 | ## Project Structure
63 |
64 | A typical project structure is as follows:
65 | ```
66 | example-apollo-graphql/
67 | ├── serverless.containers.yml # SCF project configuration file
68 | └── service/
69 | ├── Dockerfile # Multi-stage Dockerfile for Lambda and Fargate
70 | ├── package.json # Node.js project configuration and dependencies
71 | └── src/
72 | ├── index.js # Main Apollo Server and Express entrypoint
73 | └── (additional files) # GraphQL schema, resolvers, and middleware
74 | ```
75 |
76 | ## Development
77 |
78 | SCF provides a development mode that mimics the AWS environment:
79 |
80 | ```bash
81 | serverless dev
82 | ```
83 |
84 | This mode supports hot reloading and simulates API Gateway routing, enabling thorough local testing.
85 |
86 | ## Deployment
87 |
88 | Deploy your GraphQL API to AWS using:
89 |
90 | ```bash
91 | serverless deploy
92 | ```
93 |
94 | SCF will build the container image, push it to AWS ECR, and provision the necessary AWS resources (ALB, VPC, Lambda or ECS Fargate service).
95 |
96 | ## Cleanup
97 |
98 | For complete infrastructure removal, including shared networking resources:
99 | ```bash
100 | serverless remove --force --all
101 | ```
102 |
103 | ## Example Queries
104 |
105 | ```graphql
106 | # Health Check
107 | query {
108 | health
109 | }
110 |
111 | # Server Info
112 | query {
113 | info {
114 | namespace
115 | containerName
116 | stage
117 | computeType
118 | local
119 | }
120 | }
121 | ```
122 |
123 | ## Additional Resources
124 |
125 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
126 | - [Apollo Server Documentation](https://www.apollographql.com/docs/apollo-server/)
127 | - [GraphQL Documentation](https://graphql.org)
128 | - [Express Documentation](https://expressjs.com)
--------------------------------------------------------------------------------
/example-apollo-graphql/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: apollo-graphql
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | NODE_ENV: production
14 | compute:
15 | type: awsLambda # Can be switched to awsFargateEcs
16 |
--------------------------------------------------------------------------------
/example-apollo-graphql/service/Dockerfile:
--------------------------------------------------------------------------------
1 | # Base image for both Lambda and Fargate
2 | FROM node:20-alpine as base
3 |
4 | WORKDIR /app
5 | COPY package*.json ./
6 | RUN npm install
7 | COPY . .
8 |
9 | # Lambda-specific configuration
10 | FROM base as lambda
11 | # Install AWS Lambda Web Adapter
12 | RUN apk add --no-cache curl && \
13 | curl -Lo /usr/local/bin/aws-lambda-web-adapter \
14 | https://github.com/awslabs/aws-lambda-web-adapter/releases/latest/download/aws-lambda-web-adapter && \
15 | chmod +x /usr/local/bin/aws-lambda-web-adapter
16 |
17 | EXPOSE 8080
18 | CMD ["aws-lambda-web-adapter", "--port", "8080", "--", "npm", "start"]
19 |
20 | # Fargate-specific configuration
21 | FROM base as fargate
22 | EXPOSE 8080
23 | CMD ["npm", "start"]
24 |
--------------------------------------------------------------------------------
/example-apollo-graphql/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-apollo-graphql",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "start": "node src/index.js"
7 | },
8 | "dependencies": {
9 | "@apollo/server": "^4.10.0",
10 | "graphql": "^16.8.1",
11 | "express": "^4.18.2",
12 | "cors": "^2.8.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-apollo-graphql/service/src/index.js:
--------------------------------------------------------------------------------
1 | import { ApolloServer } from '@apollo/server';
2 | import { expressMiddleware } from '@apollo/server/express4';
3 | import express from 'express';
4 | import cors from 'cors';
5 |
6 | // Define GraphQL schema
7 | const typeDefs = `#graphql
8 | type ServerInfo {
9 | namespace: String
10 | containerName: String
11 | stage: String
12 | computeType: String
13 | local: String
14 | }
15 |
16 | type Query {
17 | health: String!
18 | info: ServerInfo!
19 | }
20 | `;
21 |
22 | // Define resolvers
23 | const resolvers = {
24 | Query: {
25 | health: () => 'OK',
26 | info: () => ({
27 | namespace: process.env.SERVERLESS_NAMESPACE,
28 | containerName: process.env.SERVERLESS_CONTAINER_NAME,
29 | stage: process.env.SERVERLESS_STAGE,
30 | computeType: process.env.SERVERLESS_COMPUTE_TYPE,
31 | local: process.env.SERVERLESS_LOCAL
32 | })
33 | }
34 | };
35 |
36 | const app = express();
37 | app.use(cors());
38 | app.use(express.json());
39 |
40 | // Health check endpoint (non-GraphQL)
41 | app.get('/health', (req, res) => {
42 | res.send('OK');
43 | });
44 |
45 | // Create Apollo Server
46 | const server = new ApolloServer({
47 | typeDefs,
48 | resolvers,
49 | });
50 |
51 | // Start server
52 | await server.start();
53 |
54 | // Apply middleware
55 | app.use('/', expressMiddleware(server));
56 |
57 | const port = process.env.PORT || 8080;
58 | app.listen(port, '0.0.0.0', () => {
59 | console.log(`Server running at http://localhost:${port}`);
60 | });
61 |
--------------------------------------------------------------------------------
/example-astro-static/README.md:
--------------------------------------------------------------------------------
1 | # Astro Static Site Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy an Astro-based static site that leverages Astro's modern static site generation (with optional SSR) for an optimized production deployment. The application is configured for both AWS Lambda and AWS ECS Fargate deployments using SCF.
6 |
7 | ## Features
8 |
9 | - **Astro Static Site:**
10 | Built with Astro to generate a fast, optimized, and zero-JS default static site.
11 | - **Optional Server-Side Rendering:**
12 | Supports SSR mode through Astro when dynamic rendering is needed.
13 | - **Zero-JS by Default:**
14 | Delivers pure static content with minimal or no client-side JavaScript.
15 | - **Flexible Compute Options:**
16 | Easily switch between AWS Lambda and AWS Fargate ECS deployments via SCF configuration.
17 | - **Optimized Production Builds:**
18 | Utilizes Docker multi-stage builds and production optimizations for efficient deployment.
19 |
20 | ## Prerequisites
21 |
22 | **Docker:**
23 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
24 |
25 | **Serverless Framework:**
26 | Install globally:
27 | ```bash
28 | npm i -g serverless
29 | ```
30 |
31 | **Node.js & npm:**
32 | Ensure you have a recent Node.js LTS version installed.
33 |
34 | **AWS Credentials:**
35 | Properly configure your AWS credentials (using environment variables or AWS profiles) to allow SCF to provision and update AWS resources.
36 |
37 | ## Configuration
38 |
39 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
40 |
41 | ```yaml
42 | name: astro-static
43 |
44 | deployment:
45 | type: awsApi@1.0
46 |
47 | containers:
48 | service:
49 | src: ./service
50 | routing:
51 | pathPattern: /*
52 | pathHealthCheck: /health
53 | environment:
54 | NODE_ENV: production
55 | ASTRO_TELEMETRY_DISABLED: 1 # Disable Astro telemetry
56 | compute:
57 | type: awsLambda # or awsFargateEcs
58 | ```
59 |
60 | This configuration sets:
61 | - **Project Namespace:** The project name is used as a namespace in your AWS account.
62 | - **Deployment Settings:** Configures networking (ALB, VPC, API Gateway) via the AWS API deployment type.
63 | - **Container Details:**
64 | - The source code resides in the `./service` directory.
65 | - A catch-all routing rule (`/*`) is used with a dedicated health check endpoint (`/health`).
66 | - Environment variables are set for production and to disable Astro telemetry.
67 | - The default compute type is set to `awsLambda` (switchable to `awsFargateEcs` as needed).
68 |
69 | ## Project Structure
70 |
71 | A typical project structure for this Astro static site example:
72 | ```
73 | example-astro-static/
74 | ├── serverless.containers.yml # SCF configuration file
75 | └── service/
76 | ├── astro.config.mjs # Astro configuration file
77 | ├── package.json # Node.js project configuration and dependencies
78 | ├── public/ # Static assets (images, CSS, etc.)
79 | └── src/
80 | ├── pages/ # Astro pages (including health check and index)
81 | └── (other directories) # Additional assets or components
82 | ```
83 |
84 | ## Development
85 |
86 | SCF provides a development mode that emulates AWS routing and compute environments:
87 | ```bash
88 | serverless dev
89 | ```
90 |
91 | ## Deployment
92 |
93 | Deploy your Astro static site to AWS by running:
94 | ```bash
95 | serverless deploy
96 | ```
97 |
98 | During deployment, SCF builds the container image (using the multi-stage Dockerfile) and provisions the necessary AWS resources (ALB, VPC, Lambda function, or ECS Fargate service).
99 |
100 | ## Cleanup
101 |
102 | To remove deployed AWS resources when they are no longer needed, run:
103 | ```bash
104 | serverless remove --force --all
105 | ```
106 |
107 | ## Additional Resources
108 |
109 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
110 | - [Astro Documentation](https://docs.astro.build)
111 | - [Docker Documentation](https://docs.docker.com)
112 | - [AWS Lambda Documentation](https://aws.amazon.com/lambda)
113 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
114 |
--------------------------------------------------------------------------------
/example-astro-static/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: astro-static
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | NODE_ENV: production
14 | ASTRO_TELEMETRY_DISABLED: 1 # Disable Astro telemetry
15 | compute:
16 | type: awsLambda # Can be switched to awsFargateEcs
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/content-assets.mjs:
--------------------------------------------------------------------------------
1 | export default new Map();
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/content-modules.mjs:
--------------------------------------------------------------------------------
1 | export default new Map();
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/content.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'astro:content' {
2 | export interface RenderResult {
3 | Content: import('astro/runtime/server/index.js').AstroComponentFactory;
4 | headings: import('astro').MarkdownHeading[];
5 | remarkPluginFrontmatter: Record;
6 | }
7 | interface Render {
8 | '.md': Promise;
9 | }
10 |
11 | export interface RenderedContent {
12 | html: string;
13 | metadata?: {
14 | imagePaths: Array;
15 | [key: string]: unknown;
16 | };
17 | }
18 | }
19 |
20 | declare module 'astro:content' {
21 | type Flatten = T extends { [K: string]: infer U } ? U : never;
22 |
23 | export type CollectionKey = keyof AnyEntryMap;
24 | export type CollectionEntry = Flatten;
25 |
26 | export type ContentCollectionKey = keyof ContentEntryMap;
27 | export type DataCollectionKey = keyof DataEntryMap;
28 |
29 | type AllValuesOf = T extends any ? T[keyof T] : never;
30 | type ValidContentEntrySlug = AllValuesOf<
31 | ContentEntryMap[C]
32 | >['slug'];
33 |
34 | export type ReferenceDataEntry<
35 | C extends CollectionKey,
36 | E extends keyof DataEntryMap[C] = string,
37 | > = {
38 | collection: C;
39 | id: E;
40 | };
41 | export type ReferenceContentEntry<
42 | C extends keyof ContentEntryMap,
43 | E extends ValidContentEntrySlug | (string & {}) = string,
44 | > = {
45 | collection: C;
46 | slug: E;
47 | };
48 |
49 | /** @deprecated Use `getEntry` instead. */
50 | export function getEntryBySlug<
51 | C extends keyof ContentEntryMap,
52 | E extends ValidContentEntrySlug | (string & {}),
53 | >(
54 | collection: C,
55 | // Note that this has to accept a regular string too, for SSR
56 | entrySlug: E,
57 | ): E extends ValidContentEntrySlug
58 | ? Promise>
59 | : Promise | undefined>;
60 |
61 | /** @deprecated Use `getEntry` instead. */
62 | export function getDataEntryById(
63 | collection: C,
64 | entryId: E,
65 | ): Promise>;
66 |
67 | export function getCollection>(
68 | collection: C,
69 | filter?: (entry: CollectionEntry) => entry is E,
70 | ): Promise;
71 | export function getCollection(
72 | collection: C,
73 | filter?: (entry: CollectionEntry) => unknown,
74 | ): Promise[]>;
75 |
76 | export function getEntry<
77 | C extends keyof ContentEntryMap,
78 | E extends ValidContentEntrySlug | (string & {}),
79 | >(
80 | entry: ReferenceContentEntry,
81 | ): E extends ValidContentEntrySlug
82 | ? Promise>
83 | : Promise | undefined>;
84 | export function getEntry<
85 | C extends keyof DataEntryMap,
86 | E extends keyof DataEntryMap[C] | (string & {}),
87 | >(
88 | entry: ReferenceDataEntry,
89 | ): E extends keyof DataEntryMap[C]
90 | ? Promise
91 | : Promise | undefined>;
92 | export function getEntry<
93 | C extends keyof ContentEntryMap,
94 | E extends ValidContentEntrySlug | (string & {}),
95 | >(
96 | collection: C,
97 | slug: E,
98 | ): E extends ValidContentEntrySlug
99 | ? Promise>
100 | : Promise | undefined>;
101 | export function getEntry<
102 | C extends keyof DataEntryMap,
103 | E extends keyof DataEntryMap[C] | (string & {}),
104 | >(
105 | collection: C,
106 | id: E,
107 | ): E extends keyof DataEntryMap[C]
108 | ? string extends keyof DataEntryMap[C]
109 | ? Promise | undefined
110 | : Promise
111 | : Promise | undefined>;
112 |
113 | /** Resolve an array of entry references from the same collection */
114 | export function getEntries(
115 | entries: ReferenceContentEntry>[],
116 | ): Promise[]>;
117 | export function getEntries(
118 | entries: ReferenceDataEntry[],
119 | ): Promise[]>;
120 |
121 | export function render(
122 | entry: AnyEntryMap[C][string],
123 | ): Promise;
124 |
125 | export function reference(
126 | collection: C,
127 | ): import('astro/zod').ZodEffects<
128 | import('astro/zod').ZodString,
129 | C extends keyof ContentEntryMap
130 | ? ReferenceContentEntry>
131 | : ReferenceDataEntry
132 | >;
133 | // Allow generic `string` to avoid excessive type errors in the config
134 | // if `dev` is not running to update as you edit.
135 | // Invalid collection names will be caught at build time.
136 | export function reference(
137 | collection: C,
138 | ): import('astro/zod').ZodEffects;
139 |
140 | type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T;
141 | type InferEntrySchema = import('astro/zod').infer<
142 | ReturnTypeOrOriginal['schema']>
143 | >;
144 |
145 | type ContentEntryMap = {
146 |
147 | };
148 |
149 | type DataEntryMap = {
150 |
151 | };
152 |
153 | type AnyEntryMap = ContentEntryMap & DataEntryMap;
154 |
155 | export type ContentConfig = typeof import("../src/content.config.mjs");
156 | }
157 |
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/data-store.json:
--------------------------------------------------------------------------------
1 | [["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.3.0","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":true,\"port\":8080,\"streaming\":true},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[]},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":\"shiki\",\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"responsiveImages\":false,\"serializeConfig\":false},\"legacy\":{\"collections\":false}}"]
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "_variables": {
3 | "lastUpdateCheck": 1740157149603
4 | }
5 | }
--------------------------------------------------------------------------------
/example-astro-static/service/.astro/types.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-astro-static/service/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 | import node from '@astrojs/node';
3 |
4 | /**
5 | * Astro configuration for Serverless Container Framework.
6 | *
7 | * @module AstroConfig
8 | */
9 | export default defineConfig({
10 | output: 'server',
11 | adapter: node({
12 | // Use 'standalone' mode so that the server code is bundled
13 | mode: 'standalone'
14 | }),
15 | server: {
16 | port: 8080,
17 | host: true
18 | },
19 | vite: {
20 | server: {
21 | allowedHosts: ['service'] // Named after your container name
22 | }
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/example-astro-static/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-astro-static",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro preview",
8 | "build": "astro build"
9 | },
10 | "dependencies": {
11 | "astro": "^5.3.0",
12 | "@astrojs/node": "^9.1.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-astro-static/service/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html,
8 | body {
9 | width: 100%;
10 | height: 100%;
11 | font-family: "Roboto Mono", monospace;
12 | font-optical-sizing: auto;
13 | font-weight: 400;
14 | font-style: normal;
15 | }
16 |
17 | img {
18 | -webkit-user-drag: none;
19 | }
20 |
21 | body {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | overflow: hidden; /* Prevents scrolling issues due to pseudo-element */
26 | background: #000000;
27 | color: #ffffff;
28 | }
29 |
30 | body::after {
31 | content: "";
32 | position: fixed;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | background-image: url("/images/background.png");
38 | background-size: cover;
39 | background-repeat: no-repeat;
40 | background-position: center;
41 | background-attachment: fixed;
42 | opacity: 0;
43 | animation: fadeIn 0.75s ease-in forwards;
44 | z-index: -1; /* Places background behind content */
45 | }
46 |
47 | a {
48 | color: #fd5750;
49 | text-decoration: none;
50 | }
51 |
52 | h1 {
53 | color: white;
54 | font-size: 2rem;
55 | font-weight: 500;
56 | text-align: center;
57 | margin: 20px 0;
58 | }
59 |
60 | .container {
61 | display: flex;
62 | flex-direction: column;
63 | align-items: center;
64 | width: 90%; /* Fluid width */
65 | max-width: 1200px; /* Sets a max width for larger screens */
66 | padding: 20px;
67 | text-align: center;
68 | }
69 |
70 | .logo {
71 | max-width: 400px; /* Controls logo size for responsiveness */
72 | width: 100%;
73 | height: auto;
74 | margin-bottom: 40px;
75 | opacity: 0;
76 | transform: scale(0.75); /* Start scaled at 75% */
77 | animation: fadeInZoom 2.5s ease-in-out forwards; /* 0.75s duration, 0.35s delay */
78 | }
79 |
80 | .info {
81 | display: flex;
82 | justify-content: center;
83 | align-items: center;
84 | font-size: 1.2rem;
85 | color: rgba(255, 255, 255, 0.7);
86 | margin-top: 5px;
87 | opacity: 0;
88 | animation: fadeIn 2s ease-in-out 1.5s forwards;
89 | }
90 |
91 | /**
92 | * Animations
93 | */
94 | @keyframes fadeIn {
95 | from {
96 | opacity: 0;
97 | }
98 | to {
99 | opacity: 1;
100 | }
101 | }
102 |
103 | @keyframes fadeInZoom {
104 | from {
105 | opacity: 0;
106 | transform: scale(0.85); /* Start at 85% */
107 | }
108 | to {
109 | opacity: 1;
110 | transform: scale(1); /* End at 100% */
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/example-astro-static/service/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-astro-static/service/public/images/background.png
--------------------------------------------------------------------------------
/example-astro-static/service/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-astro-static/service/public/images/favicon.png
--------------------------------------------------------------------------------
/example-astro-static/service/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-astro-static/service/public/images/logo.png
--------------------------------------------------------------------------------
/example-astro-static/service/src/pages/health.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | return new Response('OK');
3 | }
4 |
--------------------------------------------------------------------------------
/example-astro-static/service/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | /**
3 | * Astro page for Serverless Container Framework.
4 | *
5 | * This page renders the main HTML content matching the Express version.
6 | * It displays environment variables and static resources (images, CSS, etc.).
7 | *
8 | * @module IndexPage
9 | */
10 | const env = {
11 | namespace: process.env.SERVERLESS_NAMESPACE,
12 | containerName: process.env.SERVERLESS_CONTAINER_NAME,
13 | stage: process.env.SERVERLESS_STAGE,
14 | computeType: process.env.SERVERLESS_COMPUTE_TYPE,
15 | local: process.env.SERVERLESS_LOCAL
16 | };
17 | ---
18 |
19 |
20 |
21 |
22 | Serverless Container Framework
23 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |

35 |
Namespace: {env.namespace}
36 |
Container Name: {env.containerName}
37 |
Stage: {env.stage}
38 |
Compute Type: {env.computeType}
39 |
Local: {env.local}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/example-deno/README.md:
--------------------------------------------------------------------------------
1 | # Deno V2 Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) is a tool that simplifies the development and deployment of containerized applications to AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates the development and deployment of a simple, performant web application built with Deno V2, the Oak framework, and the Serverless Container Framework.
6 |
7 | ## Features
8 |
9 | - **Deno V2 Support:** Utilizes the official Deno V2 image to run your Deno application.
10 | - **Oak Framework:** Utilizes the [Oak framework](https://deno.land/x/oak), a middleware framework for handling HTTP requests.
11 | - **TypeScript:** Uses TypeScript for the application codebase.
12 | - **Compute Flexibility:** Configure the container's compute type to run either as an AWS Lambda function (`awsLambda`) or on AWS Fargate ECS (`awsFargateEcs`). A multi-stage Dockerfile is provided to support these configurations.
13 | - **Local Development:** SCF includes a rich development mode that emulates AWS ALB routing, AWS Lambda, and AWS Fargate ECS locally, allowing you to develop and test your Deno application with near-production parity.
14 |
15 | ## Prerequisites
16 |
17 | **Docker:** Install and start Docker Desktop, as it is required. Get it [here](https://www.docker.com).
18 |
19 | **Serverless Framework:** Serverless Container Framework is a feature of the Serverless Framework.
20 |
21 | ```
22 | npm i -g serverless
23 | ```
24 |
25 | **AWS Credentials:** Properly configure your AWS credentials (via environment variables or AWS profiles) to allow SCF to provision and update AWS resources. These are required to use the Serverless Container Framework.
26 |
27 | ## Configuration
28 |
29 | At the root of the example, a `serverless.containers.yml` file defines the project configuration:
30 |
31 | ```yaml
32 | name: deno
33 |
34 | deployment:
35 | type: awsApi@1.0
36 |
37 | containers:
38 | service:
39 | src: ./service
40 | routing:
41 | pathPattern: /*
42 | pathHealthCheck: /health
43 | environment:
44 | HELLO: world
45 | compute:
46 | type: awsLambda # Can be switched to awsFargateEcs
47 | build:
48 | options:
49 | - --target=awsLambda # Ensure you match the compute type set above. Sets the target build stage for the Dockerfile.
50 | ```
51 |
52 | This file specifies:
53 | - **Namespace:** The project name is `deno`, which is used as a prefix to namespace resources in your AWS account.
54 | - **Deployment Type:** Uses the AWS API deployment type to configure networking (ALB, VPC, etc.).
55 | - **Container Details:**
56 | - The source code is in the `./service` directory.
57 | - Routing rules specify a catch-all route (`/*`) with a defined health check endpoint (`/health`).
58 | - An environment variable (`HELLO`) is provided.
59 | - The default compute type is set to `awsLambda` but can be switched to `awsFargateEcs` as needed.
60 |
61 | Learn more about Configuration in the [Serverless Container Framework Documentation](https://serverless.com/containers/docs/configuration).
62 |
63 | ```
64 | example-deno/
65 | ├── serverless.containers.yml # SCF project configuration file
66 | └── service/
67 | ├── Dockerfile # Multi-stage Dockerfile for AWS Lambda and Fargate builds
68 | ├── deno.json # Deno configuration and task definitions
69 | ├── deno.lock # Deno lock file
70 | └── src/
71 | ├── index.ts # Main application entrypoint (uses Oak and oakCors)
72 | └── public/
73 | └── css/
74 | └── styles.css # Static assets (CSS, images, etc.)
75 | ```
76 |
77 | ## Development
78 |
79 | SCF includes a rich development mode that emulates AWS ALB routing, AWS Lambda, and AWS Fargate ECS locally. This mode allows you to test routing, static asset delivery, and health check endpoints while running your container with Deno, which proxies requests on port `8080`.
80 |
81 | Run the following command to start local development mode:
82 |
83 | ```bash
84 | serverless dev
85 | ```
86 |
87 | This command watches for changes and rebuilds the container as needed.
88 |
89 | Learn more about Development in the [Serverless Container Framework Documentation](https://serverless.com/containers/docs/development).
90 |
91 | ## Deployment
92 |
93 | To deploy this example with SCF, open your terminal in the `example-deno` directory.
94 |
95 | Execute the following command to deploy your container to AWS:
96 |
97 | ```bash
98 | serverless deploy
99 | ```
100 |
101 | This command builds the Deno container image using the provided Dockerfile and provisions AWS resources (ALB, VPC, Lambda function, or ECS Fargate service).
102 |
103 | If you switch compute types, ensure that you set the build `--target` option to the corresponding compute type, since the Dockerfile declares multiple build configurations.
104 |
105 | Once deployed, SCF will output the URLs and endpoints (such as the ALB endpoint) for your application.
106 |
107 | Access the application via the provided URL to see your Deno app live.
108 |
109 | To remove the deployed containers, run:
110 |
111 | ```bash
112 | serverless remove
113 | ```
114 |
115 | To remove all AWS resources, including shared infrastructure, use:
116 |
117 | ```bash
118 | serverless remove --force --all
119 | ```
120 |
121 | Learn more about Deployment in the [Serverless Container Framework Documentation](https://serverless.com/containers/docs/deployment).
122 |
123 | ## Additional Resources
124 |
125 | * [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
126 | * [Official Deno Website](https://deno.land/)
127 |
--------------------------------------------------------------------------------
/example-deno/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: deno
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | HELLO: world
14 | compute:
15 | type: awsLambda # Can be switched to awsFargateEcs
16 | build:
17 | options:
18 | - --target=awsLambda # Ensure you match the compute type set above
19 |
--------------------------------------------------------------------------------
/example-deno/service/Dockerfile:
--------------------------------------------------------------------------------
1 | # Base image for both Lambda and Fargate
2 | FROM denoland/deno:debian-2.0.0 AS base
3 | WORKDIR /app
4 | COPY . .
5 | # Generate lock file and cache all dependencies
6 | ENV DENO_DIR=/app/deno_dir
7 | RUN mkdir -p /app/deno_dir && \
8 | deno cache --lock=deno.lock src/index.ts
9 |
10 | # Lambda-specific configuration
11 | FROM debian:bookworm-slim AS awsLambda
12 | # Install Deno binary only
13 | COPY --from=denoland/deno:bin-2.0.0 /deno /usr/local/bin/deno
14 | # Copy AWS Lambda Web Adapter
15 | COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
16 | WORKDIR /app
17 | # Copy app and pre-cached dependencies
18 | COPY --from=base /app .
19 | # Set DENO_DIR for runtime (point to pre-cached directory)
20 | ENV DENO_DIR=/app/deno_dir
21 | # Set port for Lambda Web Adapter
22 | ENV PORT=8080
23 | EXPOSE 8080
24 | # Warm up (optional, shorter timeout to precompile without fetching)
25 | RUN timeout 5s deno run --allow-net --allow-env --allow-read --lock=deno.lock src/index.ts || [ $? -eq 124 ] || exit 1
26 | CMD ["deno", "run", "--allow-net", "--allow-env", "--allow-read", "--lock=deno.lock", "src/index.ts"]
27 |
28 | # Fargate-specific configuration
29 | FROM base AS awsFargateEcs
30 | EXPOSE 8080
31 | CMD ["deno", "run", "--allow-net", "--allow-env", "--allow-read", "src/index.ts"]
--------------------------------------------------------------------------------
/example-deno/service/deno.json:
--------------------------------------------------------------------------------
1 | {
2 | "imports": {
3 | "oak": "https://deno.land/x/oak@v12.6.2/mod.ts",
4 | "oak/": "https://deno.land/x/oak@v12.6.2/",
5 | "cors": "https://deno.land/x/cors@v1.2.2/mod.ts"
6 | },
7 | "tasks": {
8 | "start": "deno run --allow-net --allow-env --allow-read src/index.ts"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example-deno/service/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Application, Router } from "oak";
2 | import { oakCors } from "cors";
3 |
4 | const app = new Application();
5 | const router = new Router();
6 |
7 | /**
8 | * Middleware to enable CORS.
9 | */
10 | app.use(oakCors());
11 |
12 | /**
13 | * Middleware to add custom headers.
14 | */
15 | app.use(async (ctx, next) => {
16 | ctx.response.headers.set("x-powered-by", "serverless-container-framework");
17 | await next();
18 | });
19 |
20 | // Define routes using router
21 |
22 | /**
23 | * Health check endpoint.
24 | */
25 | router.get("/health", (ctx) => {
26 | ctx.response.body = "OK";
27 | });
28 |
29 | /**
30 | * Robots.txt endpoint.
31 | */
32 | router.get("/robots.txt", (ctx) => {
33 | ctx.response.type = "text/plain";
34 | ctx.response.body = "User-agent: *";
35 | });
36 |
37 | /**
38 | * Default route endpoint.
39 | *
40 | * This route returns the main HTML page that matches the Express app's content.
41 | */
42 | router.get("/", (ctx) => {
43 | ctx.response.type = "text/html";
44 | ctx.response.body = `
45 |
46 |
47 | Serverless Container Framework
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |

57 |
Namespace: ${Deno.env.get("SERVERLESS_NAMESPACE")}
58 |
Container Name: ${Deno.env.get("SERVERLESS_CONTAINER_NAME")}
59 |
Stage: ${Deno.env.get("SERVERLESS_STAGE")}
60 |
Compute Type: ${Deno.env.get("SERVERLESS_COMPUTE_TYPE")}
61 |
Local: ${Deno.env.get("SERVERLESS_LOCAL")}
62 |
63 |
64 |
65 | `;
66 | });
67 |
68 | // Register router middlewares
69 | app.use(router.routes());
70 | app.use(router.allowedMethods());
71 |
72 | /**
73 | * Middleware to serve static files.
74 | */
75 | app.use(async (ctx, next) => {
76 | try {
77 | await ctx.send({
78 | root: `${Deno.cwd()}/src/public`,
79 | index: "index.html"
80 | });
81 | } catch {
82 | await next();
83 | }
84 | });
85 |
86 | /**
87 | * 404 Fallback handler.
88 | *
89 | * If no route (or static file) matches the request, this middleware returns a 404 page
90 | * that matches the Express app's HTML content.
91 | */
92 | app.use((ctx) => {
93 | ctx.response.status = 404;
94 | ctx.response.type = "text/html";
95 | ctx.response.body = `
96 |
97 |
98 | 404 - Page Not Found
99 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
112 |
113 |
114 | `;
115 | });
116 |
117 | const port = 8080;
118 | console.log(`Server running at http://localhost:${port}`);
119 | await app.listen({ port });
120 |
--------------------------------------------------------------------------------
/example-deno/service/src/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html,
8 | body {
9 | width: 100%;
10 | height: 100%;
11 | font-family: "Roboto Mono", monospace;
12 | font-optical-sizing: auto;
13 | font-weight: 400;
14 | font-style: normal;
15 | }
16 |
17 | img {
18 | -webkit-user-drag: none;
19 | }
20 |
21 | body {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | overflow: hidden; /* Prevents scrolling issues due to pseudo-element */
26 | background: #000000;
27 | color: #ffffff;
28 | }
29 |
30 | body::after {
31 | content: "";
32 | position: fixed;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | background-image: url("/images/background.png");
38 | background-size: cover;
39 | background-repeat: no-repeat;
40 | background-position: center;
41 | background-attachment: fixed;
42 | opacity: 0;
43 | animation: fadeIn 0.75s ease-in forwards;
44 | z-index: -1; /* Places background behind content */
45 | }
46 |
47 | a {
48 | color: #fd5750;
49 | text-decoration: none;
50 | }
51 |
52 | h1 {
53 | color: white;
54 | font-size: 2rem;
55 | font-weight: 500;
56 | text-align: center;
57 | margin: 20px 0;
58 | }
59 |
60 | .container {
61 | display: flex;
62 | flex-direction: column;
63 | align-items: center;
64 | width: 90%; /* Fluid width */
65 | max-width: 1200px; /* Sets a max width for larger screens */
66 | padding: 20px;
67 | text-align: center;
68 | }
69 |
70 | .logo {
71 | max-width: 400px; /* Controls logo size for responsiveness */
72 | width: 100%;
73 | height: auto;
74 | margin-bottom: 40px;
75 | opacity: 0;
76 | transform: scale(0.75); /* Start scaled at 75% */
77 | animation: fadeInZoom 2.5s ease-in-out forwards; /* 0.75s duration, 0.35s delay */
78 | }
79 |
80 | .info {
81 | display: flex;
82 | justify-content: center;
83 | align-items: center;
84 | font-size: 1.2rem;
85 | color: rgba(255, 255, 255, 0.7);
86 | margin-top: 5px;
87 | opacity: 0;
88 | animation: fadeIn 2s ease-in-out 1.5s forwards;
89 | }
90 |
91 | /**
92 | * Animations
93 | */
94 | @keyframes fadeIn {
95 | from {
96 | opacity: 0;
97 | }
98 | to {
99 | opacity: 1;
100 | }
101 | }
102 |
103 | @keyframes fadeInZoom {
104 | from {
105 | opacity: 0;
106 | transform: scale(0.85); /* Start at 85% */
107 | }
108 | to {
109 | opacity: 1;
110 | transform: scale(1); /* End at 100% */
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/example-deno/service/src/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-deno/service/src/public/images/background.png
--------------------------------------------------------------------------------
/example-deno/service/src/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-deno/service/src/public/images/favicon.png
--------------------------------------------------------------------------------
/example-deno/service/src/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-deno/service/src/public/images/logo.png
--------------------------------------------------------------------------------
/example-express/README.md:
--------------------------------------------------------------------------------
1 | # Express Application Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy a simple Express-based web application using SCF. The application sets up basic routes—including a health check, static file delivery, and a fallback 404 page—with minimal configuration.
6 |
7 | ## Features
8 |
9 | - **Express Framework:**
10 | Leverages Express for routing and middleware handling.
11 | - **Static File Serving:**
12 | Serves static assets from a dedicated public directory.
13 | - **Health Check Endpoint:**
14 | Provides a simple `/health` route for monitoring application health.
15 | - **Flexible Compute Options:**
16 | Easily switch between AWS Lambda and AWS Fargate ECS deployments via SCF configuration.
17 |
18 | ## Prerequisites
19 |
20 | **Docker:**
21 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
22 |
23 | **Serverless Framework:**
24 | Install globally:
25 | ```bash
26 | npm i -g serverless
27 | ```
28 |
29 | **Node.js & npm:**
30 | Ensure you have a recent Node.js LTS version installed.
31 |
32 | **AWS Credentials:**
33 | Properly configure your AWS credentials (via environment variables or AWS profiles) to allow SCF to provision and update AWS resources.
34 |
35 | ## Configuration
36 |
37 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
38 |
39 | ```yaml
40 | name: express
41 |
42 | deployment:
43 | type: awsApi@1.0
44 |
45 | containers:
46 | service:
47 | src: ./service
48 | routing:
49 | pathPattern: /*
50 | pathHealthCheck: /health
51 | environment:
52 | HELLO: world
53 | compute:
54 | type: awsLambda # or awsFargateEcs
55 | ```
56 |
57 | This configuration sets:
58 | - **Project Namespace:** The project name (express) is used as a namespace in your AWS account.
59 | - **Deployment Settings:** Configures networking (ALB, VPC, API Gateway) via the AWS API deployment type.
60 | - **Container Details:**
61 | - The source code is located in the `./service` directory.
62 | - A catch-all routing rule (`/*`) is used with a dedicated health check endpoint (`/health`).
63 | - An environment variable (`HELLO`) is provided.
64 | - The compute type is set to `awsLambda` by default (switchable to `awsFargateEcs` as needed).
65 |
66 | ## Project Structure
67 |
68 | A typical project structure for this Express example:
69 | ```
70 | example-express/
71 | ├── serverless.containers.yml # SCF configuration file
72 | └── service/
73 | ├── package.json # Node.js project configuration and dependencies
74 | └── src/
75 | ├── index.js # Main Express application entrypoint
76 | └── public/ # Static assets (HTML, CSS, images, etc.)
77 | ```
78 |
79 | ## Development
80 |
81 | For local development, use Serverless Container Framework's development mode:
82 | ```bash
83 | serverless dev
84 | ```
85 |
86 | This will automatically start everything and set up hot reloading.
87 |
88 | ## Deployment
89 |
90 | Deploy your Express application to AWS by running:
91 | ```bash
92 | serverless deploy
93 | ```
94 |
95 | During deployment, SCF builds the container image (using the provided multi-stage Dockerfile) and provisions the necessary AWS resources (ALB, VPC, Lambda function or ECS Fargate service).
96 |
97 | ## Cleanup
98 |
99 | To remove deployed AWS resources when they are no longer needed:
100 | ```bash
101 | serverless remove --force --all
102 | ```
103 |
104 | ## Additional Resources
105 |
106 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
107 | - [Express Documentation](https://expressjs.com)
108 | - [Docker Documentation](https://docs.docker.com)
109 | - [AWS Lambda Documentation](https://aws.amazon.com/lambda)
110 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
--------------------------------------------------------------------------------
/example-express/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: express
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | HELLO: world
14 | compute:
15 | type: awsLambda # or awsFargateEcs
16 | # awsIam:
17 | # customPolicy:
18 | # Version: "2012-10-17"
19 | # Statement:
20 | # - Effect: Allow
21 | # Action:
22 | # - dynamodb:*
23 | # Resource:
24 | # - "*"
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/example-express/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-express",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.21.2"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-express/service/src/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const path = require("path");
3 | const app = express();
4 | const port = 8080;
5 |
6 | /**
7 | * Midddleware
8 | */
9 |
10 | // Serve static files from the "public" directory
11 | app.use(express.static(path.join(__dirname, "public"), { redirect: false }));
12 |
13 | app.use((req, res, next) => {
14 | // Enable CORS
15 | res.header("Access-Control-Allow-Origin", "*");
16 | res.header("Access-Control-Allow-Methods", "*");
17 | res.header("Access-Control-Allow-Headers", "*");
18 | res.header("x-powered-by", "serverless-container-framework");
19 | next();
20 | });
21 |
22 | // Enable error handling of promises
23 | const asyncHandler = (fn) => (req, res, next) => {
24 | return Promise.resolve(fn(req, res, next)).catch(next);
25 | };
26 |
27 | /**
28 | * Routes
29 | */
30 |
31 | // Robots.txt
32 | app.get("/robots.txt", (req, res) => {
33 | res.type("text/plain");
34 | res.send(`User-agent: *`);
35 | });
36 |
37 | app.options(`*`, (req, res) => {
38 | res.status(200).send();
39 | });
40 |
41 | // Healthcheck
42 | app.get(`/health`, (req, res) => {
43 | res.status(200).send(`OK`);
44 | });
45 |
46 | // Default
47 | app.get(
48 | `/*`,
49 | asyncHandler((req, res) => {
50 | res.send(`
51 |
52 |
53 | Serverless Container Framework
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |

63 |
Namespace: ${process.env.SERVERLESS_NAMESPACE}
64 |
Container Name: ${process.env.SERVERLESS_CONTAINER_NAME}
65 |
Stage: ${process.env.SERVERLESS_STAGE}
66 |
Compute Type: ${process.env.SERVERLESS_COMPUTE_TYPE}
67 |
Local: ${process.env.SERVERLESS_LOCAL}
68 |
69 |
70 |
71 | `);
72 | })
73 | );
74 |
75 | // Catch-all 404 - for any unmatched paths
76 | app.use((req, res) => {
77 | res.status(404).send(`
78 |
79 |
80 | 404 - Page Not Found
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
92 |
93 |
94 | `);
95 | });
96 |
97 | /**
98 | * Error Handler
99 | */
100 | app.use((err, req, res, next) => {
101 | console.log(err);
102 | res.status(err.status || 500).json({
103 | error: err.message || "Internal Server Error",
104 | message: err.message || "Internal Server Error",
105 | code: err.code || "internal_error",
106 | status: err.status,
107 | // stack: err.stack - Don't include stack trace
108 | });
109 | });
110 |
111 | app.listen(port, "0.0.0.0", () => {
112 | console.log(`App initialized`);
113 | });
114 |
--------------------------------------------------------------------------------
/example-express/service/src/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html,
8 | body {
9 | width: 100%;
10 | height: 100%;
11 | font-family: "Roboto Mono", monospace;
12 | font-optical-sizing: auto;
13 | font-weight: 400;
14 | font-style: normal;
15 | }
16 |
17 | img {
18 | -webkit-user-drag: none;
19 | }
20 |
21 | body {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | overflow: hidden; /* Prevents scrolling issues due to pseudo-element */
26 | background: #000000;
27 | color: #ffffff;
28 | }
29 |
30 | body::after {
31 | content: "";
32 | position: fixed;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | background-image: url("/images/background.png");
38 | background-size: cover;
39 | background-repeat: no-repeat;
40 | background-position: center;
41 | background-attachment: fixed;
42 | opacity: 0;
43 | animation: fadeIn 0.75s ease-in forwards;
44 | z-index: -1; /* Places background behind content */
45 | }
46 |
47 | a {
48 | color: #fd5750;
49 | text-decoration: none;
50 | }
51 |
52 | h1 {
53 | color: white;
54 | font-size: 2rem;
55 | font-weight: 500;
56 | text-align: center;
57 | margin: 20px 0;
58 | }
59 |
60 | .container {
61 | display: flex;
62 | flex-direction: column;
63 | align-items: center;
64 | width: 90%; /* Fluid width */
65 | max-width: 1200px; /* Sets a max width for larger screens */
66 | padding: 20px;
67 | text-align: center;
68 | }
69 |
70 | .logo {
71 | max-width: 400px; /* Controls logo size for responsiveness */
72 | width: 100%;
73 | height: auto;
74 | margin-bottom: 40px;
75 | opacity: 0;
76 | transform: scale(0.75); /* Start scaled at 75% */
77 | animation: fadeInZoom 2.5s ease-in-out forwards; /* 0.75s duration, 0.35s delay */
78 | }
79 |
80 | .info {
81 | display: flex;
82 | justify-content: center;
83 | align-items: center;
84 | font-size: 1.2rem;
85 | color: rgba(255, 255, 255, 0.7);
86 | margin-top: 5px;
87 | opacity: 0;
88 | animation: fadeIn 2s ease-in-out 1.5s forwards;
89 | }
90 |
91 | /**
92 | * Animations
93 | */
94 | @keyframes fadeIn {
95 | from {
96 | opacity: 0;
97 | }
98 | to {
99 | opacity: 1;
100 | }
101 | }
102 |
103 | @keyframes fadeInZoom {
104 | from {
105 | opacity: 0;
106 | transform: scale(0.85); /* Start at 85% */
107 | }
108 | to {
109 | opacity: 1;
110 | transform: scale(1); /* End at 100% */
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/example-express/service/src/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-express/service/src/public/images/background.png
--------------------------------------------------------------------------------
/example-express/service/src/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-express/service/src/public/images/favicon.png
--------------------------------------------------------------------------------
/example-express/service/src/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-express/service/src/public/images/logo.png
--------------------------------------------------------------------------------
/example-hono/README.md:
--------------------------------------------------------------------------------
1 | # Hono Application Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy a Hono-based web application using SCF. Hono is a lightweight framework for building fast HTTP services. The application sets up basic routes—including static file delivery, a health check, and a fallback 404 page—and can be deployed on AWS Lambda or AWS Fargate ECS with minimal configuration.
6 |
7 | ## Features
8 |
9 | - **Hono Framework:**
10 | Leverages Hono for a fast and minimalistic HTTP service.
11 | - **Static File Serving:**
12 | Serves static content from a dedicated public directory.
13 | - **Health Check Endpoint:**
14 | Provides a reliable `/health` route to verify application status.
15 | - **Flexible Compute Options:**
16 | Easily deployable as an AWS Lambda function or on AWS Fargate ECS.
17 | - **Lightweight & Efficient:**
18 | Designed for minimal overhead and optimal performance in a containerized environment.
19 |
20 | ## Prerequisites
21 |
22 | **Docker:**
23 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
24 |
25 | **Serverless Framework:**
26 | Install globally:
27 | ```bash
28 | npm i -g serverless
29 | ```
30 |
31 | **Node.js & npm:**
32 | Ensure you have a recent Node.js LTS version installed.
33 |
34 | **AWS Credentials:**
35 | Properly configure your AWS credentials (via environment variables or AWS profiles) to enable SCF to provision and update AWS resources.
36 |
37 | ## Configuration
38 |
39 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
40 |
41 | ```yaml
42 | name: hono
43 |
44 | deployment:
45 | type: awsApi@1.0
46 |
47 | containers:
48 | service:
49 | src: ./service
50 | routing:
51 | pathPattern: /*
52 | pathHealthCheck: /health
53 | environment:
54 | HELLO: world
55 | compute:
56 | type: awsLambda # or awsFargateEcs
57 | ```
58 |
59 | This configuration sets:
60 | - **Project Namespace:** The project name (hono) is used to namespace resources in your AWS account.
61 | - **Deployment Settings:** Configures networking via the AWS API deployment type.
62 | - **Container Details:**
63 | - The source code is located in the `./service` directory.
64 | - A catch-all routing rule (`/*`) is used with a designated health check endpoint (`/health`).
65 | - An environment variable (`HELLO`) is provided.
66 | - The compute type is set to `awsLambda` by default (or can be switched to `awsFargateEcs`).
67 |
68 | ## Project Structure
69 |
70 | A typical project structure for this Hono example:
71 | ```
72 | example-hono/
73 | ├── serverless.containers.yml # SCF configuration file
74 | └── service/
75 | ├── package.json # Node.js project configuration and dependencies
76 | └── src/
77 | ├── index.js # Main Hono application entrypoint
78 | └── public/ # Static assets (HTML, CSS, images, etc.)
79 | ```
80 |
81 | ## Development
82 |
83 | For local development, you can run the Hono application using SCF's development mode which emulates AWS routing and compute environments:
84 | ```bash
85 | serverless dev
86 | ```
87 |
88 | This will automatically start everything and set up hot reloading.
89 |
90 | ## Deployment
91 |
92 | Deploy your Hono application to AWS using:
93 | ```bash
94 | serverless deploy
95 | ```
96 |
97 | During deployment, SCF builds the container image (using the provided multi-stage Dockerfile) and provisions the necessary AWS resources (ALB, VPC, Lambda function, or ECS Fargate service).
98 |
99 | ## Cleanup
100 |
101 | To remove deployed AWS resources when they are no longer needed, run:
102 | ```bash
103 | serverless remove --force --all
104 | ```
105 |
106 | ## Additional Resources
107 |
108 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
109 | - [Hono Documentation](https://hono.dev)
110 | - [Docker Documentation](https://docs.docker.com)
111 | - [AWS Lambda Documentation](https://aws.amazon.com/lambda)
112 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
--------------------------------------------------------------------------------
/example-hono/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: hono
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | HELLO: world
14 | compute:
15 | type: awsLambda # or awsFargateEcs
16 | # awsIam:
17 | # customPolicy:
18 | # Version: "2012-10-17"
19 | # Statement:
20 | # - Effect: Allow
21 | # Action:
22 | # - dynamodb:*
23 | # Resource:
24 | # - "*"
25 |
--------------------------------------------------------------------------------
/example-hono/service/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "service-hono",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "service-hono",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@hono/node-server": "^1.13.3",
13 | "hono": "^4.6.8"
14 | }
15 | },
16 | "node_modules/@hono/node-server": {
17 | "version": "1.13.3",
18 | "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.13.3.tgz",
19 | "integrity": "sha512-tEo3hcyQ6chvSnJ3tKzfX4z2sd7Q+ZkBwwBdW1Ya8Mz29dukxC2xcWiB/lAMwGJrYMW8QTgknIsLu1AsnMBe7A==",
20 | "engines": {
21 | "node": ">=18.14.1"
22 | },
23 | "peerDependencies": {
24 | "hono": "^4"
25 | }
26 | },
27 | "node_modules/hono": {
28 | "version": "4.6.8",
29 | "resolved": "https://registry.npmjs.org/hono/-/hono-4.6.8.tgz",
30 | "integrity": "sha512-f+2Ec9JAzabT61pglDiLJcF/DjiSefZkjCn9bzm1cYLGkD5ExJ3Jnv93ax9h0bn7UPLHF81KktoyjdQfWI2n1Q==",
31 | "engines": {
32 | "node": ">=16.9.0"
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example-hono/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "service-hono",
3 | "type": "module",
4 | "version": "1.0.0",
5 | "description": "",
6 | "main": "src/index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@hono/node-server": "^1.13.3",
14 | "hono": "^4.6.8"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example-hono/service/src/index.js:
--------------------------------------------------------------------------------
1 | import { Hono } from "hono";
2 | import { serveStatic } from "@hono/node-server/serve-static";
3 | import { serve } from "@hono/node-server";
4 |
5 | const app = new Hono();
6 |
7 | /**
8 | * Middleware
9 | */
10 |
11 | // Middleware to set headers
12 | app.use("*", async (c, next) => {
13 | // Enable CORS
14 | c.header("Access-Control-Allow-Origin", "*");
15 | c.header("Access-Control-Allow-Methods", "*");
16 | c.header("Access-Control-Allow-Headers", "*");
17 | c.header("x-powered-by", "serverless-container-framework");
18 | await next();
19 | });
20 |
21 | // Serve static files from the "public" directory
22 | app.use(
23 | "/*",
24 | serveStatic({
25 | root: "src/public",
26 | })
27 | );
28 |
29 | /**
30 | * Routes
31 | */
32 |
33 | // Robots.txt
34 | app.get("/robots.txt", (c) => c.text("User-agent: *"));
35 |
36 | // Options
37 | app.options("*", (c) => c.status(200).body(""));
38 |
39 | // Healthcheck
40 | app.get(`/health`, (c) => c.text(`OK`));
41 |
42 | // Default Route
43 | app.get(`/*`, (c) =>
44 | c.html(`
45 |
46 |
47 | Serverless Container Framework
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |

57 |
Namespace: ${process.env.SERVERLESS_NAMESPACE}
58 |
Container Name: ${process.env.SERVERLESS_CONTAINER_NAME}
59 |
Stage: ${process.env.SERVERLESS_STAGE}
60 |
Compute Type: ${process.env.SERVERLESS_COMPUTE_TYPE}
61 |
Local: ${process.env.SERVERLESS_LOCAL}
62 |
63 |
64 |
65 | `)
66 | );
67 |
68 | // Catch-all 404 Handler
69 | app.notFound((c) =>
70 | c.html(
71 | `
72 |
73 |
74 | 404 - Page Not Found
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
86 |
87 |
88 | `,
89 | 404
90 | )
91 | );
92 |
93 | /**
94 | * Error Handler
95 | */
96 | app.onError((err, c) => {
97 | console.error(err);
98 | const status = err.status || 500;
99 | return c.json(
100 | {
101 | error: err.message || "Internal Server Error",
102 | message: err.message || "Internal Server Error",
103 | code: err.code || "internal_error",
104 | status: status,
105 | // stack: err.stack, // Don't include stack trace
106 | },
107 | status
108 | );
109 | });
110 |
111 | serve({ fetch: app.fetch, port: process.env.PORT || 8080 });
112 |
--------------------------------------------------------------------------------
/example-hono/service/src/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | html,
8 | body {
9 | width: 100%;
10 | height: 100%;
11 | font-family: "Roboto Mono", monospace;
12 | font-optical-sizing: auto;
13 | font-weight: 400;
14 | font-style: normal;
15 | }
16 |
17 | img {
18 | -webkit-user-drag: none;
19 | }
20 |
21 | body {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | overflow: hidden; /* Prevents scrolling issues due to pseudo-element */
26 | background: #000000;
27 | color: #ffffff;
28 | }
29 |
30 | body::after {
31 | content: "";
32 | position: fixed;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | background-image: url("/images/background.png");
38 | background-size: cover;
39 | background-repeat: no-repeat;
40 | background-position: center;
41 | background-attachment: fixed;
42 | opacity: 0;
43 | animation: fadeIn 0.75s ease-in forwards;
44 | z-index: -1; /* Places background behind content */
45 | }
46 |
47 | a {
48 | color: #fd5750;
49 | text-decoration: none;
50 | }
51 |
52 | h1 {
53 | color: white;
54 | font-size: 2rem;
55 | font-weight: 500;
56 | text-align: center;
57 | margin: 20px 0;
58 | }
59 |
60 | .container {
61 | display: flex;
62 | flex-direction: column;
63 | align-items: center;
64 | width: 90%; /* Fluid width */
65 | max-width: 1200px; /* Sets a max width for larger screens */
66 | padding: 20px;
67 | text-align: center;
68 | }
69 |
70 | .logo {
71 | max-width: 400px; /* Controls logo size for responsiveness */
72 | width: 100%;
73 | height: auto;
74 | margin-bottom: 40px;
75 | opacity: 0;
76 | transform: scale(0.75); /* Start scaled at 75% */
77 | animation: fadeInZoom 2.5s ease-in-out forwards; /* 0.75s duration, 0.35s delay */
78 | }
79 |
80 | .info {
81 | display: flex;
82 | justify-content: center;
83 | align-items: center;
84 | font-size: 1.2rem;
85 | color: rgba(255, 255, 255, 0.7);
86 | margin-top: 5px;
87 | opacity: 0;
88 | animation: fadeIn 2s ease-in-out 1.5s forwards;
89 | }
90 |
91 | /**
92 | * Animations
93 | */
94 | @keyframes fadeIn {
95 | from {
96 | opacity: 0;
97 | }
98 | to {
99 | opacity: 1;
100 | }
101 | }
102 |
103 | @keyframes fadeInZoom {
104 | from {
105 | opacity: 0;
106 | transform: scale(0.85); /* Start at 85% */
107 | }
108 | to {
109 | opacity: 1;
110 | transform: scale(1); /* End at 100% */
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/example-hono/service/src/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-hono/service/src/public/images/background.png
--------------------------------------------------------------------------------
/example-hono/service/src/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-hono/service/src/public/images/favicon.png
--------------------------------------------------------------------------------
/example-hono/service/src/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-hono/service/src/public/images/logo.png
--------------------------------------------------------------------------------
/example-mastra-slack/.env-example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | SLACK_APP_NAME=
3 | SLACK_SIGNING_SECRET=
4 | SLACK_BOT_TOKEN=
5 | SLACK_APP_TOKEN=
6 | DB_URL=
7 |
--------------------------------------------------------------------------------
/example-mastra-slack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mastra-slack-agent",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "dev": "mastra dev",
8 | "build": "mastra build"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "description": "",
14 | "type": "module",
15 | "engines": {
16 | "node": ">=20.9.0"
17 | },
18 | "dependencies": {
19 | "@ai-sdk/openai": "^1.3.22",
20 | "@mastra/core": "^0.10.0",
21 | "@mastra/loggers": "^0.10.0",
22 | "@mastra/memory": "^0.10.0",
23 | "@mastra/pg": "^0.10.0",
24 | "@slack/bolt": "^4.4.0",
25 | "zod": "^3.25.20"
26 | },
27 | "devDependencies": {
28 | "@types/node": "^22.15.21",
29 | "mastra": "^0.10.0",
30 | "typescript": "^5.8.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example-mastra-slack/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: mastra-slack
2 |
3 | deployment:
4 | type: "aws@1.0"
5 |
6 | containers:
7 | agent:
8 | src: ./
9 | compute:
10 | type: awsFargateEcs
11 | awsFargateEcs:
12 | cpu: 1024
13 | memory: 2048
14 | routing:
15 | pathPattern: "/*"
16 | pathHealthCheck: "/"
17 | environment:
18 | OPENAI_API_KEY: ${env:OPENAI_API_KEY}
19 | PORT: 8080
20 | SLACK_APP_NAME: ${env:SLACK_APP_NAME}
21 | SLACK_SIGNING_SECRET: ${env:SLACK_SIGNING_SECRET}
22 | SLACK_BOT_TOKEN: ${env:SLACK_BOT_TOKEN}
23 | SLACK_APP_TOKEN: ${env:SLACK_APP_TOKEN}
24 | DB_URL: ${env:DB_URL}
25 | integrations:
26 | slack:
27 | type: slack
28 | name: ${env:SLACK_APP_NAME}
29 |
--------------------------------------------------------------------------------
/example-mastra-slack/src/mastra/agents/weather-agent.ts:
--------------------------------------------------------------------------------
1 | import { openai } from '@ai-sdk/openai';
2 | import { Agent } from '@mastra/core/agent';
3 | import { Memory } from '@mastra/memory';
4 | import { weatherTool } from '../tools/weather-tool';
5 | import { storage } from '../store';
6 |
7 | export const weatherAgent = new Agent({
8 | name: 'Weather Agent',
9 | instructions: `
10 | You are a helpful weather assistant that provides accurate weather information.
11 |
12 | Your primary function is to help users get weather details for specific locations. When responding:
13 | - Always ask for a location if none is provided
14 | - If the location name isn’t in English, please translate it
15 | - If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
16 | - Include relevant details like humidity, wind conditions, and precipitation
17 | - Keep responses concise but informative
18 |
19 | Use the weatherTool to fetch current weather data.
20 | `,
21 | model: openai('gpt-4o-mini'),
22 | tools: { weatherTool },
23 | memory: new Memory({
24 | storage: storage,
25 | }),
26 | });
27 |
--------------------------------------------------------------------------------
/example-mastra-slack/src/mastra/index.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Mastra } from '@mastra/core/mastra';
3 | import { PinoLogger } from '@mastra/loggers';
4 | import { storage } from './store';
5 | import crypto from 'crypto';
6 |
7 | import type { Context } from 'hono';
8 | import { registerApiRoute } from '@mastra/core/server';
9 |
10 | import bolt from "@slack/bolt";
11 | import type { AgentGenerateOptions } from "@mastra/core/agent";
12 |
13 | import { weatherAgent } from './agents/weather-agent';
14 |
15 | const socketMode = process.env.SERVERLESS_LOCAL === "true";
16 |
17 | const slackConfig: bolt.AppOptions = {
18 | token: process.env.SLACK_BOT_TOKEN,
19 | signingSecret: process.env.SLACK_SIGNING_SECRET,
20 | appToken: process.env.SLACK_APP_TOKEN,
21 | socketMode,
22 | }
23 |
24 | const boltApp = new bolt.App(slackConfig);
25 |
26 | const eventHandler = async ({
27 | event,
28 | context,
29 | say,
30 | }: {
31 | event: bolt.KnownEventFromType<"message">;
32 | context: bolt.Context;
33 | say: (params: bolt.SayArguments) => Promise;
34 | }) => {
35 | const isDM = event.channel_type === "im";
36 |
37 | const textMessage = (event as {text?: string}).text;
38 | const isMention = textMessage?.includes(`<@${context.botUserId}>`);
39 |
40 | const shouldRespond = isDM || isMention;
41 |
42 | if(shouldRespond) {
43 | const responseThreadId = "thread_ts" in event ? event.thread_ts : event.ts;
44 |
45 | const agentOptions: AgentGenerateOptions = {
46 | resourceId: context.botUserId || context.botId || "bot",
47 | threadId: responseThreadId!,
48 | };
49 |
50 | let responseMessage = "";
51 |
52 | try {
53 | const generateOptions = { ...agentOptions, output: undefined }
54 | const result = await weatherAgent.generate(textMessage!, generateOptions)
55 | responseMessage = result.text;
56 | } catch (error) {
57 | responseMessage = `Error occurred while calling Agent: ${(error as Error).message}`
58 | }
59 |
60 | await say({
61 | text: responseMessage,
62 | thread_ts: responseThreadId,
63 | })
64 | }
65 |
66 | }
67 |
68 | boltApp.event("message", eventHandler);
69 |
70 | const verifySlackSignature = async (c: Context) => {
71 | const slackTimestamp = c.req.header("x-slack-request-timestamp");
72 | const slackSignature = c.req.header("x-slack-signature");
73 | const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 60 * 5;
74 |
75 | if (!slackTimestamp || Number(slackTimestamp) < fiveMinutesAgo)
76 | return false;
77 |
78 | const sigBasestring = `v0:${slackTimestamp}:${await c.req.text()}`;
79 | const hmac = crypto.createHmac("sha256", slackConfig.signingSecret!);
80 | hmac.update(sigBasestring);
81 | const mySignature = `v0=${hmac.digest("hex")}`;
82 | return crypto.timingSafeEqual(
83 | Buffer.from(mySignature),
84 | Buffer.from(slackSignature!),
85 | );
86 | }
87 |
88 | const requestHandler = async (c: Context) => {
89 | const body = await c.req.json();
90 |
91 | // Verify slack signature before we continue
92 | if(!(await verifySlackSignature(c))) {
93 | return c.json({ error: 'Invalid Slack signature' }, 401);
94 | }
95 |
96 | if(body.type === 'url_verification') {
97 | return c.json({challenge: body.challenge})
98 | }
99 |
100 | let ackCalled = false;
101 | let ackResponse: Response | undefined;
102 | const ackFn = async (response?: unknown) => {
103 | if(ackCalled) {
104 | return;
105 | }
106 |
107 | ackCalled = true;
108 |
109 | if(response instanceof Error) {
110 | ackResponse = c.json({error: response.message}, 500)
111 | } else if (!response) {
112 | ackResponse = c.text("")
113 | } else {
114 | ackResponse = c.json(response)
115 | }
116 | }
117 |
118 | if (body.type === 'event_callback') {
119 | const response = c.json({status: 'ok'})
120 |
121 | const event: bolt.ReceiverEvent = {
122 | body,
123 | ack: ackFn,
124 | }
125 |
126 | boltApp.processEvent(event).catch((error) => {
127 | console.error("Error processing event", error)
128 | })
129 |
130 | return response;
131 | } else {
132 | return ackResponse || c.json({status: 'ok'})
133 | }
134 | }
135 |
136 | const apiRoutes = socketMode ? [] : [
137 | registerApiRoute('/slack',{
138 | method: 'POST',
139 | handler: requestHandler
140 | })
141 | ]
142 |
143 | export const mastra = new Mastra({
144 | agents: { weatherAgent },
145 | storage: storage,
146 | logger: new PinoLogger({
147 | name: 'Mastra',
148 | level: 'info',
149 | }),
150 | server: {
151 | port: process.env.PORT ? parseInt(process.env.PORT) : 8080,
152 | host: '0.0.0.0',
153 | apiRoutes
154 | }
155 | });
156 |
157 | if(socketMode) {
158 | await boltApp.start();
159 | }
--------------------------------------------------------------------------------
/example-mastra-slack/src/mastra/store.ts:
--------------------------------------------------------------------------------
1 | import { PostgresStore } from '@mastra/pg';
2 |
3 | export const storage = new PostgresStore({
4 | connectionString: process.env.DB_URL!,
5 | });
6 |
--------------------------------------------------------------------------------
/example-mastra-slack/src/mastra/tools/weather-tool.ts:
--------------------------------------------------------------------------------
1 | import { createTool } from '@mastra/core/tools';
2 | import { z } from 'zod';
3 |
4 | interface GeocodingResponse {
5 | results: {
6 | latitude: number;
7 | longitude: number;
8 | name: string;
9 | }[];
10 | }
11 | interface WeatherResponse {
12 | current: {
13 | time: string;
14 | temperature_2m: number;
15 | apparent_temperature: number;
16 | relative_humidity_2m: number;
17 | wind_speed_10m: number;
18 | wind_gusts_10m: number;
19 | weather_code: number;
20 | };
21 | }
22 |
23 | export const weatherTool = createTool({
24 | id: 'get-weather',
25 | description: 'Get current weather for a location',
26 | inputSchema: z.object({
27 | location: z.string().describe('City name'),
28 | }),
29 | outputSchema: z.object({
30 | temperature: z.number(),
31 | feelsLike: z.number(),
32 | humidity: z.number(),
33 | windSpeed: z.number(),
34 | windGust: z.number(),
35 | conditions: z.string(),
36 | location: z.string(),
37 | }),
38 | execute: async ({ context }) => {
39 | return await getWeather(context.location);
40 | },
41 | });
42 |
43 | const getWeather = async (location: string) => {
44 | const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
45 | const geocodingResponse = await fetch(geocodingUrl);
46 | const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
47 |
48 | if (!geocodingData.results?.[0]) {
49 | throw new Error(`Location '${location}' not found`);
50 | }
51 |
52 | const { latitude, longitude, name } = geocodingData.results[0];
53 |
54 | const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
55 |
56 | const response = await fetch(weatherUrl);
57 | const data = (await response.json()) as WeatherResponse;
58 |
59 | return {
60 | temperature: data.current.temperature_2m,
61 | feelsLike: data.current.apparent_temperature,
62 | humidity: data.current.relative_humidity_2m,
63 | windSpeed: data.current.wind_speed_10m,
64 | windGust: data.current.wind_gusts_10m,
65 | conditions: getWeatherCondition(data.current.weather_code),
66 | location: name,
67 | };
68 | };
69 |
70 | function getWeatherCondition(code: number): string {
71 | const conditions: Record = {
72 | 0: 'Clear sky',
73 | 1: 'Mainly clear',
74 | 2: 'Partly cloudy',
75 | 3: 'Overcast',
76 | 45: 'Foggy',
77 | 48: 'Depositing rime fog',
78 | 51: 'Light drizzle',
79 | 53: 'Moderate drizzle',
80 | 55: 'Dense drizzle',
81 | 56: 'Light freezing drizzle',
82 | 57: 'Dense freezing drizzle',
83 | 61: 'Slight rain',
84 | 63: 'Moderate rain',
85 | 65: 'Heavy rain',
86 | 66: 'Light freezing rain',
87 | 67: 'Heavy freezing rain',
88 | 71: 'Slight snow fall',
89 | 73: 'Moderate snow fall',
90 | 75: 'Heavy snow fall',
91 | 77: 'Snow grains',
92 | 80: 'Slight rain showers',
93 | 81: 'Moderate rain showers',
94 | 82: 'Violent rain showers',
95 | 85: 'Slight snow showers',
96 | 86: 'Heavy snow showers',
97 | 95: 'Thunderstorm',
98 | 96: 'Thunderstorm with slight hail',
99 | 99: 'Thunderstorm with heavy hail',
100 | };
101 | return conditions[code] || 'Unknown';
102 | }
103 |
--------------------------------------------------------------------------------
/example-mastra-slack/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "ES2022",
5 | "moduleResolution": "bundler",
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "skipLibCheck": true,
10 | "noEmit": true,
11 | "outDir": "dist"
12 | },
13 | "include": [
14 | "src/**/*"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/example-nextjs/README.md:
--------------------------------------------------------------------------------
1 | # Next.js Application Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy a Next.js web application using SCF. Due to the potential for large SSR outputs, this project is configured for deployment on AWS Fargate ECS.
6 |
7 | ## Features
8 |
9 | - **Next.js Framework:**
10 | Leverages Next.js for server-side rendering (SSR) and static site generation.
11 | - **Dynamic Routing & SSR:**
12 | Provides robust routing and dynamic page generation.
13 | - **Optimized Production Builds:**
14 | Docker multi-stage builds ensure efficient deployments.
15 | - **Flexible Compute Options:**
16 | Configured for AWS Fargate ECS to handle large HTML responses.
17 |
18 | ## Prerequisites
19 |
20 | **Docker:**
21 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
22 |
23 | **Serverless Framework:**
24 | Install globally:
25 | ```bash
26 | npm i -g serverless
27 | ```
28 |
29 | **Node.js & npm:**
30 | Ensure you have a recent Node.js LTS version installed.
31 |
32 | **AWS Credentials:**
33 | Configure your AWS credentials (via environment variables or profiles) for SCF deployments.
34 |
35 | ## Configuration
36 |
37 | At the project root, the `serverless.containers.yml` file defines the SCF configuration:
38 |
39 | ```yaml
40 | name: nextjs
41 |
42 | deployment:
43 | type: awsApi@1.0
44 |
45 | containers:
46 | service:
47 | src: ./service
48 | routing:
49 | pathPattern: /*
50 | pathHealthCheck: /health
51 | environment:
52 | HELLO: world
53 | compute:
54 | # awsLambda is not recommended for this Next.js app.
55 | # SSR can generate large HTML responses, which may exceed
56 | # the req/res size limits for AWS Lambda.
57 | type: awsFargateEcs
58 | ```
59 |
60 | ## Project Structure
61 |
62 | A typical project structure for this Next.js example:
63 | ```
64 | example-nextjs/
65 | ├── serverless.containers.yml # SCF configuration file
66 | └── service/
67 | ├── next.config.ts # Next.js configuration file
68 | ├── package.json # Project configuration and dependencies
69 | ├── public/ # Static assets (images, CSS, etc.)
70 | └── src/
71 | ├── app/ # Next.js app folder (pages, components, etc.)
72 | └── (other directories) # Additional assets and logic
73 | ```
74 |
75 | ## Development
76 |
77 | For local development, use Serverless Container Framework's development mode:
78 | ```bash
79 | serverless dev
80 | ```
81 | This will automatically start the Next.js development server with hot reloading and AWS emulation. It detects the dev npm script and uses that for hot reloading.
82 |
83 | ## Deployment
84 |
85 | Deploy your Next.js application to AWS by running:
86 | ```bash
87 | serverless deploy
88 | ```
89 | SCF builds the container image (using the provided multi-stage Dockerfile) and provisions the necessary AWS resources.
90 |
91 | ## Cleanup
92 |
93 | To remove the deployed AWS resources, run:
94 | ```bash
95 | serverless remove --force --all
96 | ```
97 |
98 | ## Additional Resources
99 |
100 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
101 | - [Next.js Documentation](https://nextjs.org/docs)
102 | - [Docker Documentation](https://docs.docker.com)
103 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
--------------------------------------------------------------------------------
/example-nextjs/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: nextjs
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | HELLO: world
14 | compute:
15 | # awsLambda is not recommended for this nextjs app.
16 | # SSR can generate large HTML responses, which may exceed
17 | # the req/res size limit for AWS ALB and AWS Lambda.
18 | type: awsFargateEcs
19 |
--------------------------------------------------------------------------------
/example-nextjs/service/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/example-nextjs/service/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { dirname } from "path";
2 | import { fileURLToPath } from "url";
3 | import { FlatCompat } from "@eslint/eslintrc";
4 |
5 | const __filename = fileURLToPath(import.meta.url);
6 | const __dirname = dirname(__filename);
7 |
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | });
11 |
12 | const eslintConfig = [
13 | ...compat.extends("next/core-web-vitals", "next/typescript"),
14 | {
15 | rules: {
16 | "react/react-in-jsx-scope": "off",
17 | },
18 | },
19 | ];
20 |
21 | export default eslintConfig;
22 |
--------------------------------------------------------------------------------
/example-nextjs/service/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/example-nextjs/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --turbopack",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "^19.0.0",
13 | "react-dom": "^19.0.0",
14 | "next": "15.1.2"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^19",
20 | "@types/react-dom": "^19",
21 | "eslint": "^9",
22 | "eslint-config-next": "15.1.2",
23 | "@eslint/eslintrc": "^3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/example-nextjs/service/public/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-nextjs/service/public/images/background.png
--------------------------------------------------------------------------------
/example-nextjs/service/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-nextjs/service/public/images/favicon.png
--------------------------------------------------------------------------------
/example-nextjs/service/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless/containers/e80134de4a176f42fd2055292874d71ca1a05485/example-nextjs/service/public/images/logo.png
--------------------------------------------------------------------------------
/example-nextjs/service/src/app/about/page.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * About page component that provides information about the application.
3 | *
4 | * @returns {JSX.Element} The rendered About page.
5 | */
6 | export default function About() {
7 | return (
8 |
9 |
About Page
10 |
This is a minimal React app with basic routing.
11 |
12 | );
13 | }
--------------------------------------------------------------------------------
/example-nextjs/service/src/app/components/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | /**
4 | * NavBar component that displays fixed top navigation links.
5 | *
6 | * @returns {JSX.Element} The rendered navigation bar.
7 | */
8 | export default function NavBar() {
9 | return (
10 |
11 |
25 |
26 | );
27 | }
--------------------------------------------------------------------------------
/example-nextjs/service/src/app/globals.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Global CSS updated to reflect the React app styles.
3 | */
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | }
9 |
10 | html,
11 | body {
12 | width: 100%;
13 | height: 100%;
14 | font-family: "Roboto Mono", monospace;
15 | font-optical-sizing: auto;
16 | font-weight: 400;
17 | font-style: normal;
18 | }
19 |
20 | img {
21 | -webkit-user-drag: none;
22 | }
23 |
24 | body {
25 | display: flex;
26 | align-items: center;
27 | justify-content: center;
28 | overflow: hidden; /* Prevents scrolling issues due to pseudo-element */
29 | background: #000000;
30 | color: #ffffff;
31 | }
32 |
33 | body::after {
34 | content: "";
35 | position: fixed;
36 | top: 0;
37 | left: 0;
38 | width: 100%;
39 | height: 100%;
40 | background-image: url("/images/background.png");
41 | background-size: cover;
42 | background-repeat: no-repeat;
43 | background-position: center;
44 | background-attachment: fixed;
45 | opacity: 0;
46 | animation: fadeIn 0.75s ease-in forwards;
47 | z-index: -1; /* Places background behind content */
48 | }
49 |
50 | a {
51 | color: #fd5750;
52 | text-decoration: none;
53 | }
54 |
55 | h1 {
56 | color: white;
57 | font-size: 2rem;
58 | font-weight: 500;
59 | text-align: center;
60 | margin: 20px 0;
61 | }
62 |
63 | .container {
64 | display: flex;
65 | flex-direction: column;
66 | align-items: center;
67 | width: 90%; /* Fluid width */
68 | max-width: 1200px; /* Sets a max width for larger screens */
69 | padding: 20px;
70 | text-align: center;
71 | }
72 |
73 | .logo {
74 | max-width: 400px; /* Controls logo size for responsiveness */
75 | width: 100%;
76 | height: auto;
77 | margin-bottom: 40px;
78 | opacity: 0;
79 | transform: scale(0.75); /* Start scaled at 75% */
80 | animation: fadeInZoom 2.5s ease-in-out forwards; /* 2.5s duration for zoom effect */
81 | }
82 |
83 | .info {
84 | display: flex;
85 | justify-content: center;
86 | align-items: center;
87 | font-size: 1.2rem;
88 | color: rgba(255, 255, 255, 0.7);
89 | margin-top: 5px;
90 | opacity: 0;
91 | animation: fadeIn 2s ease-in-out 1.5s forwards;
92 | }
93 |
94 | /**
95 | * Animations
96 | */
97 | @keyframes fadeIn {
98 | from {
99 | opacity: 0;
100 | }
101 | to {
102 | opacity: 1;
103 | }
104 | }
105 |
106 | @keyframes fadeInZoom {
107 | from {
108 | opacity: 0;
109 | transform: scale(0.85); /* Start at 85% */
110 | }
111 | to {
112 | opacity: 1;
113 | transform: scale(1); /* End at 100% */
114 | }
115 | }
116 |
117 | /**
118 | * Styles for the fixed top navigation bar used for routing.
119 | */
120 | .nav-header {
121 | position: fixed;
122 | top: 0;
123 | width: 100%;
124 | padding: 10px 0;
125 | z-index: 1000;
126 | }
127 |
128 | .nav-ul {
129 | display: flex;
130 | justify-content: center;
131 | list-style: none;
132 | margin: 0;
133 | padding: 0;
134 | }
135 |
136 | .nav-li {
137 | margin: 0 15px;
138 | }
139 |
140 | .nav-link {
141 | color: #fff;
142 | text-decoration: none;
143 | }
144 |
145 | .nav-link:hover {
146 | text-decoration: underline;
147 | }
148 |
149 | /* Main content top padding to avoid content being hidden behind the fixed nav bar */
150 | main {
151 | padding-top: 60px;
152 | }
153 |
154 | .navbar {
155 | position: fixed;
156 | top: 1rem;
157 | right: 2rem;
158 | display: flex;
159 | gap: 1rem;
160 | z-index: 50;
161 | }
--------------------------------------------------------------------------------
/example-nextjs/service/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Geist, Geist_Mono } from "next/font/google";
3 | import NavBar from "./components/NavBar";
4 | import "./globals.css";
5 |
6 | const geistSans = Geist({
7 | variable: "--font-geist-sans",
8 | subsets: ["latin"],
9 | });
10 |
11 | const geistMono = Geist_Mono({
12 | variable: "--font-geist-mono",
13 | subsets: ["latin"],
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Serverless Container Framework - NextJS Example",
18 | description: "Serverless Container Framework NextJS Example",
19 | icons: {
20 | icon: [
21 | { url: '/images/favicon.png' },
22 | // { url: '/images/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
23 | // { url: '/images/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
24 | ],
25 | // If you have an Apple touch icon, you can add it too
26 | apple: [
27 | { url: '/images/apple-touch-icon.png' },
28 | ],
29 | },
30 | };
31 |
32 | export default function RootLayout({
33 | children,
34 | }: Readonly<{
35 | children: React.ReactNode;
36 | }>) {
37 | return (
38 |
39 |
42 |
43 | {children}
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/example-nextjs/service/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Image from "next/image";
4 |
5 | /**
6 | * Home page component that displays the main landing content.
7 | *
8 | * @returns {JSX.Element} The rendered Home page.
9 | */
10 | export default function Home() {
11 | return (
12 |
13 |
20 |
21 | );
22 | }
--------------------------------------------------------------------------------
/example-nextjs/service/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "noEmit": true,
13 | "esModuleInterop": true,
14 | "module": "esnext",
15 | "moduleResolution": "bundler",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "preserve",
19 | "incremental": true,
20 | "plugins": [
21 | {
22 | "name": "next"
23 | }
24 | ],
25 | "paths": {
26 | "@/*": [
27 | "./src/*"
28 | ]
29 | }
30 | },
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | ".next/types/**/*.ts"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/example-react-router-v7/README.md:
--------------------------------------------------------------------------------
1 | # React Router V7 Application Example for Serverless Container Framework
2 |
3 | [Serverless Container Framework (SCF)](https://serverless.com/containers/docs) simplifies the development and deployment of containerized applications on AWS Lambda and/or AWS Fargate ECS.
4 |
5 | This example demonstrates how to build and deploy a full‑stack React application using React Router v7 for dynamic routing and server-side rendering (SSR). It is optimized for deployment on AWS Fargate ECS to handle potentially large HTML responses.
6 |
7 | ## Features
8 |
9 | - **React Router v7:**
10 | Utilizes React Router v7 for advanced routing and SSR capabilities.
11 | - **Full‑Stack React Application:**
12 | Combines client‑side navigation with server‑side rendering for optimal performance.
13 | - **Optimized Bundling:**
14 | Built using modern bundling tools (e.g., Vite and React Router dev) for efficient production builds.
15 | - **Flexible Compute Options:**
16 | Configured for AWS Fargate ECS to manage larger HTML responses beyond AWS Lambda's limits.
17 |
18 | ## Prerequisites
19 |
20 | **Docker:**
21 | Install and start Docker Desktop. ([Get Docker](https://www.docker.com))
22 |
23 | **Serverless Framework:**
24 | Install globally:
25 | ```bash
26 | npm i -g serverless
27 | ```
28 |
29 | **Node.js & npm:**
30 | Ensure you have a recent Node.js LTS version installed.
31 |
32 | **AWS Credentials:**
33 | Set up your AWS credentials (via environment variables or profiles) for SCF deployments.
34 |
35 | ## Configuration
36 |
37 | The SCF configuration is defined in the `serverless.containers.yml` file at the project root:
38 |
39 | ```yaml
40 | name: rrouter-v7
41 |
42 | deployment:
43 | type: awsApi@1.0
44 |
45 | containers:
46 | service:
47 | src: ./service
48 | routing:
49 | pathPattern: /*
50 | pathHealthCheck: /health
51 | environment:
52 | HELLO: world
53 | compute:
54 | # awsLambda is not recommended for react-router-v7
55 | # due to potential large HTML responses.
56 | type: awsFargateEcs
57 | ```
58 |
59 | ## Project Structure
60 |
61 | A typical project structure for this React Router v7 example:
62 | ```
63 | example-react-router-v7/
64 | ├── serverless.containers.yml # SCF configuration file
65 | └── service/
66 | ├── package.json # Project configuration and dependencies
67 | ├── Dockerfile # Dockerfile for building the application container
68 | └── app/ # Application source code
69 | ├── routes/ # React Router route components
70 | ├── welcome/ # Welcome components and assets
71 | ├── app.css # Main CSS styles (e.g., Tailwind CSS)
72 | ├── root.tsx # Root component and layout
73 | └── (other assets and configuration files)
74 | ```
75 |
76 | ## Development
77 |
78 | For local development, use Serverless Container Framework's development mode:
79 | ```bash
80 | serverless dev
81 | ```
82 | This will automatically start the development environment with hot reloading and AWS-like routing. It detects the dev npm script and uses that for hot reloading.
83 |
84 | ## Deployment
85 |
86 | Deploy your React Router v7 application to AWS by running:
87 | ```bash
88 | serverless deploy
89 | ```
90 | SCF builds the container image using Docker multi-stage builds and provisions the necessary AWS resources (ALB, VPC, and ECS Fargate service).
91 |
92 | ## Cleanup
93 |
94 | To remove the deployed AWS resources, run:
95 | ```bash
96 | serverless remove --force --all
97 | ```
98 |
99 | ## Additional Resources
100 |
101 | - [Serverless Container Framework Documentation](https://serverless.com/containers/docs)
102 | - [React Router v7 Documentation](https://reactrouter.com/)
103 | - [React Documentation](https://reactjs.org/docs/getting-started.html)
104 | - [Vite Documentation](https://vitejs.dev)
105 | - [Docker Documentation](https://docs.docker.com)
106 | - [AWS Fargate Documentation](https://aws.amazon.com/fargate)
--------------------------------------------------------------------------------
/example-react-router-v7/serverless.containers.yml:
--------------------------------------------------------------------------------
1 | name: rrouter-v7
2 |
3 | deployment:
4 | type: awsApi@1.0
5 |
6 | containers:
7 | service:
8 | src: ./service
9 | routing:
10 | pathPattern: /*
11 | pathHealthCheck: /health
12 | environment:
13 | HELLO: world
14 | compute:
15 | # awsLambda is not recommended for react-router-v7.
16 | # SSR can generate large HTML responses, which may exceed
17 | # the req/res size limit for AWS ALB and AWS Lambda.
18 | type: awsFargateEcs
--------------------------------------------------------------------------------
/example-react-router-v7/service/.dockerignore:
--------------------------------------------------------------------------------
1 | .react-router
2 | build
3 | node_modules
4 | README.md
--------------------------------------------------------------------------------
/example-react-router-v7/service/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 |
4 | # React Router
5 | /.react-router/
6 | /build/
7 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine AS development-dependencies-env
2 | COPY . /app
3 | WORKDIR /app
4 | RUN npm ci
5 |
6 | FROM node:20-alpine AS production-dependencies-env
7 | COPY ./package.json package-lock.json /app/
8 | WORKDIR /app
9 | RUN npm ci --omit=dev
10 |
11 | FROM node:20-alpine AS build-env
12 | COPY . /app/
13 | COPY --from=development-dependencies-env /app/node_modules /app/node_modules
14 | WORKDIR /app
15 | RUN npm run build
16 |
17 | FROM node:20-alpine
18 | COPY ./package.json package-lock.json /app/
19 | COPY --from=production-dependencies-env /app/node_modules /app/node_modules
20 | COPY --from=build-env /app/build /app/build
21 | WORKDIR /app
22 | CMD ["npm", "run", "start"]
--------------------------------------------------------------------------------
/example-react-router-v7/service/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to React Router!
2 |
3 | A modern, production-ready template for building full-stack React applications using React Router.
4 |
5 | [](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
6 |
7 | ## Features
8 |
9 | - 🚀 Server-side rendering
10 | - ⚡️ Hot Module Replacement (HMR)
11 | - 📦 Asset bundling and optimization
12 | - 🔄 Data loading and mutations
13 | - 🔒 TypeScript by default
14 | - 🎉 TailwindCSS for styling
15 | - 📖 [React Router docs](https://reactrouter.com/)
16 |
17 | ## Getting Started
18 |
19 | ### Installation
20 |
21 | Install the dependencies:
22 |
23 | ```bash
24 | npm install
25 | ```
26 |
27 | ### Development
28 |
29 | Start the development server with HMR:
30 |
31 | ```bash
32 | npm run dev
33 | ```
34 |
35 | Your application will be available at `http://localhost:5173`.
36 |
37 | ## Building for Production
38 |
39 | Create a production build:
40 |
41 | ```bash
42 | npm run build
43 | ```
44 |
45 | ## Deployment
46 |
47 | ### Docker Deployment
48 |
49 | This template includes three Dockerfiles optimized for different package managers:
50 |
51 | - `Dockerfile` - for npm
52 | - `Dockerfile.pnpm` - for pnpm
53 | - `Dockerfile.bun` - for bun
54 |
55 | To build and run using Docker:
56 |
57 | ```bash
58 | # For npm
59 | docker build -t my-app .
60 |
61 | # For pnpm
62 | docker build -f Dockerfile.pnpm -t my-app .
63 |
64 | # For bun
65 | docker build -f Dockerfile.bun -t my-app .
66 |
67 | # Run the container
68 | docker run -p 3000:3000 my-app
69 | ```
70 |
71 | The containerized application can be deployed to any platform that supports Docker, including:
72 |
73 | - AWS ECS
74 | - Google Cloud Run
75 | - Azure Container Apps
76 | - Digital Ocean App Platform
77 | - Fly.io
78 | - Railway
79 |
80 | ### DIY Deployment
81 |
82 | If you're familiar with deploying Node applications, the built-in app server is production-ready.
83 |
84 | Make sure to deploy the output of `npm run build`
85 |
86 | ```
87 | ├── package.json
88 | ├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
89 | ├── build/
90 | │ ├── client/ # Static assets
91 | │ └── server/ # Server-side code
92 | ```
93 |
94 | ## Styling
95 |
96 | This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
97 |
98 | ---
99 |
100 | Built with ❤️ using React Router.
101 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/app/app.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @theme {
4 | --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
5 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6 | }
7 |
8 | html,
9 | body {
10 | @apply bg-white dark:bg-gray-950;
11 |
12 | @media (prefers-color-scheme: dark) {
13 | color-scheme: dark;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/app/root.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | isRouteErrorResponse,
3 | Links,
4 | Meta,
5 | Outlet,
6 | Scripts,
7 | ScrollRestoration,
8 | } from "react-router";
9 |
10 | import type { Route } from "./+types/root";
11 | import "./app.css";
12 |
13 | export const links: Route.LinksFunction = () => [
14 | { rel: "preconnect", href: "https://fonts.googleapis.com" },
15 | {
16 | rel: "preconnect",
17 | href: "https://fonts.gstatic.com",
18 | crossOrigin: "anonymous",
19 | },
20 | {
21 | rel: "stylesheet",
22 | href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
23 | },
24 | ];
25 |
26 | export function Layout({ children }: { children: React.ReactNode }) {
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {children}
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default function App() {
45 | return ;
46 | }
47 |
48 | export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
49 | let message = "Oops!";
50 | let details = "An unexpected error occurred.";
51 | let stack: string | undefined;
52 |
53 | if (isRouteErrorResponse(error)) {
54 | message = error.status === 404 ? "404" : "Error";
55 | details =
56 | error.status === 404
57 | ? "The requested page could not be found."
58 | : error.statusText || details;
59 | } else if (import.meta.env.DEV && error && error instanceof Error) {
60 | details = error.message;
61 | stack = error.stack;
62 | }
63 |
64 | return (
65 |
66 | {message}
67 | {details}
68 | {stack && (
69 |
70 | {stack}
71 |
72 | )}
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/app/routes.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type RouteConfig,
3 | route,
4 | index,
5 | } from "@react-router/dev/routes";
6 |
7 | export default [
8 | index("./routes/home.tsx"),
9 | route("health", "./routes/health.tsx"),
10 | route("about", "./routes/about.tsx"),
11 | ] satisfies RouteConfig;
12 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/app/routes/about.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from "./+types/home";
2 |
3 | export function meta({}: Route.MetaArgs) {
4 | return [
5 | { title: "Serverless Container Framework - Example React Router v7" },
6 | { name: "description", content: "Serverless Container Framework example using React Router v7" },
7 | ];
8 | }
9 |
10 | export default function Home() {
11 | return A simple about page
;
12 | }
13 |
--------------------------------------------------------------------------------
/example-react-router-v7/service/app/routes/health.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Simple health check route component that renders a minimal page and returns 200 status
3 | * @returns {JSX.Element} Health check page
4 | */
5 | export default function Health() {
6 | return (
7 |
8 |
OK
9 |
10 | );
11 | }
12 |
13 | /**
14 | * Loader function to set status code for health check
15 | * @returns {Response} Response with 200 status code
16 | */
17 | export function loader() {
18 | return new Response("OK", {
19 | status: 200,
20 | headers: {
21 | "Content-Type": "text/plain",
22 | },
23 | });
24 | }
25 |
26 | /**
27 | * Metadata configuration for the health check route
28 | * @returns {Array