NexosNexos

nexos.yaml — AI Agent Spec

This page is written for AI coding assistants. It is the complete, self-contained spec for generating a working nexos.yaml (and, when needed, a Dockerfile.nexos) for any repository. A human reader can use the human reference instead.

Instructions for AI agents

Follow the procedure below exactly. Produce two artifacts at most: nexos.yaml (required) and Dockerfile.nexos (only if the repo has no usable Dockerfile). Do not add comments unless they explain a non-obvious choice. Do not invent fields not listed in the schema.

What Nexos does

Nexos turns every git branch into a live preview environment. On each push, it reads nexos.yaml from the commit, builds the Docker image, provisions the managed services declared in the file, starts the containers, and exposes a preview URL at {branch}-{name}.nexos.rs.

Your job is to produce a nexos.yaml that makes the repository run correctly in that environment, plus a Dockerfile.nexos if the existing build does not satisfy the requirements below.

Procedure

  1. Read the repo. Look at package.json, pyproject.toml, requirements.txt, go.mod, Cargo.toml, Gemfile, composer.json, pom.xml, framework configs, and any existing Dockerfile.
  2. Detect the stack and port (table below). If the app reads process.env.PORT / os.environ["PORT"] already, use whatever default the framework uses; Nexos will set $PORT to match the app.port you declare.
  3. Detect managed services from imports / connection string references in code (table below). Enable each one in services:. Do not enable services the app does not use.
  4. Detect migrations. If you find Prisma, Drizzle, Knex, TypeORM, Alembic, Django migrate, Rails db:migrate, or similar, set the corresponding command as hooks.on_deploy.
  5. Detect seed scripts. If package.json has a seed script or there is a clear one-time seeder, set it as hooks.on_new.
  6. Check the Dockerfile. If there is no Dockerfile, or it fails the requirements below, create Dockerfile.nexos and point build.dockerfile at it. Otherwise, leave build.dockerfile as the existing path.
  7. Write the smallest nexos.yaml that works. Skip any field where the default is correct. Do not output buildResources or app.cpu/memory unless you have evidence the defaults are wrong.

Stack and port detection

StackDetect byDefault port
Next.jsnext in package.json deps3000
Node/Express/Fastifyexpress / fastify in deps3000
NestJS@nestjs/* in deps3000
Djangomanage.py + Django in requirements.txt / pyproject8000
Flask/FastAPIflask / fastapi in requirements.txt8000
RailsGemfile with rails3000
Go (net/http, chi, gin, echo)go.mod present8080
Rust (axum, actix)Cargo.toml present8080
Spring Bootpom.xml or build.gradle with spring-boot8080

If the codebase calls app.listen(NUMBER) or equivalent with a literal port, use that number instead of the default. Apps that read $PORT can keep any port — Nexos sets the env var to match app.port.

Service detection

Code signalsservices entryAuto-injected env
pg, postgres, prisma, drizzle, knex, sequelize, typeorm, sqlalchemy with postgresql://, psycopg2, pg gempostgresDATABASE_URL
redis, ioredis, redis-py, redis gem, bullmq, sidekiqredisREDIS_URL
mongodb, mongoose, pymongo, mongoidmongodbMONGODB_URL
@elastic/elasticsearch, elasticsearch-py, elasticsearch-railselasticsearchELASTICSEARCH_URL
@aws-sdk/client-s3, boto3 S3, aws-sdk s3, miniominioS3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET
nodemailer, smtplib, mail gem (dev/test config)mailhogSMTP_HOST, SMTP_PORT

The injected env vars above always exist when the corresponding service is enabled — do not add them to the env: block. If the app reads a differently-named variable (e.g. POSTGRES_URL), add an entry in env: that aliases it: POSTGRES_URL: $DATABASE_URL — but only if you verified the app reads the alternate name.

When to create Dockerfile.nexos

Prefer the existing Dockerfile. Only create Dockerfile.nexos if at least one of these is true:

  • There is no Dockerfile in the repo.
  • The existing Dockerfile hardcodes a port via EXPOSE + CMD and the app does not read $PORT.
  • The existing Dockerfile is dev-only (e.g. installs dev dependencies and runs npm run dev, mounts source as a volume, depends on docker-compose links).
  • The existing Dockerfile assumes services on localhost rather than using injected env vars.
  • The existing Dockerfile fails to install production dependencies or omits the build step.

Name the file exactly Dockerfile.nexos, place it at the repo root, and set build.dockerfile: ./Dockerfile.nexos in nexos.yaml.

Dockerfile.nexos requirements

  • Multi-stage when the language has a build step (Node, Go, Rust, Java, .NET). Final stage must be slim/alpine and contain only runtime artifacts.
  • Production dependencies only in the final stage (e.g. npm ci --omit=dev, pip install --no-cache-dir).
  • Do not hardcode the port. The container must listen on $PORT. Use CMD ["sh", "-c", "..."] if the runtime cannot expand env vars on its own.
  • Do not assume localhost services. Read $DATABASE_URL, $REDIS_URL, etc. — Nexos injects them.
  • Use a non-root user when the base image makes it straightforward (Node alpine: USER node).
  • Order layers by stability (deps first, source last) so BuildKit caching helps on incremental builds.

Example Dockerfile.nexos (Node/Next.js)

# Dockerfile.nexos
# Multi-stage Node.js example. Adapt for your stack.
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/.next ./.next
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./
# Nexos sets $PORT — do not hardcode it.
EXPOSE 3000
CMD ["sh", "-c", "node server.js"]

Complete schema

Every field below is optional. Output only the fields you need; omit the rest. Values shown are the defaults.

# nexos.yaml — full schema (every field is optional)
name: string                          # project display name

build:
  dockerfile: ./Dockerfile            # path; use ./Dockerfile.nexos if you create one
  context: .
  args:                               # plaintext build args (NOT for secrets)
    KEY: value

app:                                  # the main runtime container
  port: 3000                          # injected as $PORT
  cpu: 1                              # cores, 0.1–16
  memory: 512                         # MB, 64–65536

buildResources:                       # limits during image build
  cpu: 2                              # cores, 0.5–16
  memory: 2048                        # MB, 256–65536
  timeout: 600                        # seconds, 60–3600

services:                             # managed addons (one per environment)
  postgres: true | false | { enabled, version, storageMb }
  redis:    true | false | { enabled, version }
  mongodb:  true | false | { enabled, version, storageMb }
  elasticsearch: true | false | { enabled, version, storageMb }
  minio:    true | false | { enabled, storageMb }
  mailhog:  true | false

hooks:
  on_deploy: string | null            # runs every deploy (e.g. migrations)
  on_new: string | null               # runs once on brand-new environments

env:                                  # shared env vars (encrypted at rest)
  KEY: value                          # string | number | boolean

containers:                           # extra containers (workers, sidecars, jobs)
  - name: lowercase-with-hyphens      # required
    dockerfile: ./path/Dockerfile
    context: .
    port: 3001
    cpu: 0.5
    memory: 256
    public: false                     # true -> own preview subdomain
    command: "node worker.js"         # overrides image CMD
    type: service | job               # job = runs once per deploy
    depends_on: [other-container]
    default: false                    # true -> preview URL routes here
    run_hooks: false                  # true -> hooks run in this container
    env:
      KEY: value

nodePool:
  type: shared | private              # default: shared

Constraints and rules

  • Strict schema. Unknown top-level fields are rejected. Do not invent fields.
  • No secrets in committed YAML. Put API keys, OAuth secrets, database passwords for external services, etc. in the dashboard (encrypted at rest). The env: block in the committed file is for non-secret config (NODE_ENV, LOG_LEVEL, feature flags).
  • Do not duplicate auto-injected env vars. DATABASE_URL, REDIS_URL, PORT, and the other service URLs are set automatically.
  • Container names in containers: must match ^[a-z0-9-]+$.
  • At most one entry in containers: may set default: true, and at most one run_hooks: true.
  • Type=job containers must declare command.
  • Resource ranges: cpu 0.1–16, memory 64–65536 MB, port 1–65535, buildResources.timeout 60–3600 seconds.

Decision examples

Next.js app with Prisma + Postgres + Redis cache

name: my-app

build:
  dockerfile: ./Dockerfile

app:
  port: 3000

services:
  postgres: true
  redis: true

hooks:
  on_deploy: npx prisma migrate deploy

Django app, no existing Dockerfile

Create Dockerfile.nexos using the official python:3.12-slim base, install requirements.txt, run gunicorn project.wsgi --bind 0.0.0.0:$PORT. Then:

name: my-django-app

build:
  dockerfile: ./Dockerfile.nexos

app:
  port: 8000

services:
  postgres: true

hooks:
  on_deploy: python manage.py migrate --noinput
  on_new: python manage.py loaddata fixtures/seed.json

Node API with a separate worker

name: my-api

app:
  port: 3000

services:
  postgres: true
  redis: true

containers:
  - name: worker
    dockerfile: ./Dockerfile
    command: node dist/worker.js
    cpu: 0.25
    memory: 256

hooks:
  on_deploy: npm run db:migrate

Output format

Write nexos.yaml to the repo root. If you created a Nexos-specific Dockerfile, write it as Dockerfile.nexos at the repo root. Do not modify the existing Dockerfile. Do not write any other files. Do not add code comments to nexos.yaml unless they explain a non-obvious decision (e.g. why a specific Postgres version is pinned). After writing, tell the user to commit and push — Nexos applies the file on the next push.