Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/alien-cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ const KNOWN_TEMPLATES: &[(&str, &str)] = &[
"webhook-api-ts",
"Receive webhooks and expose an API inside the customer's cloud.",
),
(
"nextjs-app",
"Deploy a Next.js app as a single container in the customer's cloud.",
),
];

fn fallback_templates() -> Vec<TemplateInfo> {
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Each example is a self-contained template you can initialize with `alien init`.
| [data-connector-ts](./data-connector-ts) | Query private databases behind the customer's firewall. | TypeScript |
| [event-pipeline-ts](./event-pipeline-ts) | Process events from queues, storage changes, and cron schedules. | TypeScript |
| [webhook-api-ts](./webhook-api-ts) | Receive webhooks and expose an API inside the customer's cloud. | TypeScript |
| [nextjs-app](./nextjs-app) | Deploy a Next.js app as a single container in the customer's cloud. | TypeScript |

## Getting started

Expand Down
6 changes: 6 additions & 0 deletions examples/nextjs-app/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.next
.alien
alien.ts
template.toml
README.md
14 changes: 14 additions & 0 deletions examples/nextjs-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Node
node_modules/
package-lock.json
pnpm-lock.yaml

# Next.js
.next/
next-env.d.ts

# Alien
.alien/

# OS
.DS_Store
19 changes: 19 additions & 0 deletions examples/nextjs-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install
Comment on lines +3 to +4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Non-deterministic dependency installs in Docker build

package-lock.json is listed in the template's .gitignore, so the file is never committed and therefore never present when alien release builds this image. COPY package-lock.json* ./ silently copies nothing, and npm install then resolves semver ranges at build time, meaning successive alien release runs can produce images with different transitive dependency versions. Every other Node.js template in this repo has the same gitignore entry, but here it is the primary production build path. Consider generating and committing a package-lock.json for the example (removing it from the example's .gitignore), or switching the build stage to pnpm so it shares the workspace lock file.

Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/nextjs-app/Dockerfile
Line: 3-4

Comment:
**Non-deterministic dependency installs in Docker build**

`package-lock.json` is listed in the template's `.gitignore`, so the file is never committed and therefore never present when `alien release` builds this image. `COPY package-lock.json* ./` silently copies nothing, and `npm install` then resolves semver ranges at build time, meaning successive `alien release` runs can produce images with different transitive dependency versions. Every other Node.js template in this repo has the same gitignore entry, but here it is the primary production build path. Consider generating and committing a `package-lock.json` for the example (removing it from the example's `.gitignore`), or switching the build stage to pnpm so it shares the workspace lock file.

How can I resolve this? If you propose a fix, please make it concise.

COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
# .next/static and public are not part of the standalone output and must be
# copied alongside it for server.js to serve them.
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
ENV HOSTNAME=0.0.0.0
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]
43 changes: 43 additions & 0 deletions examples/nextjs-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Next.js App

The smallest containerized Next.js app: one container, one page, one API route. Use it as the starting point for dashboards, internal tools, or any web app that needs to run where the customer's data lives.

The app builds with the included Dockerfile (Next.js [standalone output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output)) and runs as a single replica behind an HTTPS load balancer.

## What's included

| Resource | Type | Description |
|----------|------|-------------|
| `app` | Container | The Next.js app, built from the Dockerfile and exposed over HTTP |

## Local development

Run the Next.js dev server directly:

```bash
npm install
npm run dev
```

Then check it works:

```bash
curl http://localhost:3000/api/health
# {"status":"ok"}

open http://localhost:3000
```

## Deploying

```bash
alien deploy production --platform aws # or gcp / azure
```

Alien builds the container image from the Dockerfile, pushes it, and provisions the compute and load balancer. The deploy output prints the public URL.

## Learn more

- [Quickstart guide](https://alien.dev/docs/quickstart) -- build a worker, test locally, send remote commands
- [How Alien Works](https://alien.dev/docs/how-alien-works) -- stacks, isolated areas, push vs pull
- [Stacks](https://alien.dev/docs/stacks) -- workers, storage, queues, vaults
18 changes: 18 additions & 0 deletions examples/nextjs-app/alien.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as alien from "@alienplatform/core"

const app = new alien.Container("app")
.code({ type: "source", src: ".", toolchain: { type: "docker", dockerfile: "Dockerfile" } })
.cpu(0.5)
.memory("512Mi")
.port(3000)
.expose("http")
// Next's standalone server reads these; HOSTNAME=0.0.0.0 binds all interfaces.
.environment({ PORT: "3000", HOSTNAME: "0.0.0.0" })
.permissions("app")
.build()

export default new alien.Stack("nextjs-app")
.platforms(["aws", "gcp", "azure"])
.add(app, "live")
.permissions({ profiles: { app: {} } }) // no linked resources → empty profile
.build()
3 changes: 3 additions & 0 deletions examples/nextjs-app/app/api/health/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export async function GET() {
return Response.json({ status: "ok" })
}
15 changes: 15 additions & 0 deletions examples/nextjs-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Metadata } from "next"
import type { ReactNode } from "react"

export const metadata: Metadata = {
title: "Next.js on Alien",
description: "The smallest containerized Next.js app deployed with Alien.",
}

export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
14 changes: 14 additions & 0 deletions examples/nextjs-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function Home() {
return (
<main style={{ fontFamily: "sans-serif", maxWidth: "40rem", margin: "4rem auto" }}>
<h1>Next.js on Alien</h1>
<p>
This app runs as a single container inside the cloud account it was deployed to. Edit{" "}
<code>app/page.tsx</code> and redeploy to ship changes.
</p>
<p>
Try the API route at <a href="/api/health">/api/health</a>.
</p>
</main>
)
}
8 changes: 8 additions & 0 deletions examples/nextjs-app/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { NextConfig } from "next"

const nextConfig: NextConfig = {
// The container runs the generated .next/standalone/server.js without node_modules.
output: "standalone",
}

export default nextConfig
21 changes: 21 additions & 0 deletions examples/nextjs-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "nextjs-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build"
Comment on lines +6 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing start script for local production testing

The Dockerfile runs node server.js directly, but there is no start script for users who want to preview the standalone output locally before deploying. Without it, users must know to run node .next/standalone/server.js manually. A start script keeps parity with the README's npm install && npm run dev workflow and matches community expectations for Next.js projects.

Suggested change
"dev": "next dev",
"build": "next build"
"dev": "next dev",
"build": "next build",
"start": "node .next/standalone/server.js"
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/nextjs-app/package.json
Line: 6-7

Comment:
**Missing `start` script for local production testing**

The Dockerfile runs `node server.js` directly, but there is no `start` script for users who want to preview the standalone output locally before deploying. Without it, users must know to run `node .next/standalone/server.js` manually. A `start` script keeps parity with the README's `npm install && npm run dev` workflow and matches community expectations for Next.js projects.

```suggestion
    "dev": "next dev",
    "build": "next build",
    "start": "node .next/standalone/server.js"
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

},
"dependencies": {
"next": "^16.2.9",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@alienplatform/core": "^1.7.0",
"@types/node": "^24.0.15",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.8.3"
}
}
2 changes: 2 additions & 0 deletions examples/nextjs-app/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *
Allow: /
3 changes: 3 additions & 0 deletions examples/nextjs-app/template.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = "nextjs-app"
description = "Deploy a Next.js app as a single container in the customer's cloud."
language = "TypeScript"
30 changes: 30 additions & 0 deletions examples/nextjs-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules"]
}
Loading
Loading