Docs/Deployments/Configuration

Configuration (nexlayer.yaml)

The nexlayer.yaml file describes your entire deployment in a clean, readable format. Your AI assistant will generate this for you — but understanding it helps you customize and debug your deployments.

Designed to be Simple

Nexlayer's YAML format is intentionally minimal. No boilerplate, no repetition, no 500-line config files. Just describe your app and its services — Nexlayer handles the rest. This simplicity is core to how Nexlayer works with AI assistants.

Anatomy of a nexlayer.yaml

Every YAML has three main parts — think of them as building blocks:

Application Name

Your app's unique identifier. Add a url field for production deployments.

Pods (Services)

Your app's containers — frontend, API, database, etc. Each pod runs independently and can communicate with others.

Configuration

Ports, environment variables, persistent storage, and URL paths for each service.

Basic Structure
application:
  name: my-app           # Your app's unique name
  url: www.mysite.com    # Optional: for production deployment

pods:
  - name: web            # Service name
    image: nginx:latest  # Docker image to run
    path: /              # URL path (/ = homepage)
    servicePorts: [80]   # Port(s) your app listens on

Key Concepts

Pods

Think of pods as individual services. A typical app might have a frontend pod, an API pod, and a database pod. Pods communicate using internal DNS:podname.pod

Images

Docker images from Docker Hub (public) or GitHub Container Registry (private with auth). Your AI assistant can help you build and push images.

Paths

URL routes for each service. path: / handles the homepage, path: /api handles API endpoints.

Volumes

Persistent storage that survives restarts. Essential for databases and file uploads. Specify size and mount path. Any pod with a volumes entry is automatically upgraded to a StatefulSet, whether or not resourceType is set.

Secrets

Sensitive values stored securely by Nexlayer. Injected as environment variables (default) or mounted as read-only files. Manage via the dashboard or the secrets field in your YAML.

nexlayer.yaml v2.0

New

v2.0 adds workload types beyond the default deployment — StatefulSets, DaemonSets, Jobs, and CronJobs — plus resource requests/limits and subdomain routing.

Opt in to v2.0

Every field below is inactive until you set application.version: 2.0, placed above name. Using a v2 field without it doesn't error — Nexlayer silently ignores the field and warns you to add the version.

application:
  version: 2.0     # Must come above name. Accepts a number or numeric string: 2, 2.0, "2.0"
  name: my-app

resourceType

Sets the workload type for a pod. Defaults to deployment when omitted.

ValueUse case
deployment (default)Stateless — replicas are interchangeable
statefulsetStable identity + durable per-instance storage — Postgres, MySQL, Mongo, Redis cluster, Kafka
daemonsetOne pod per node — log shippers, metrics collectors, network sidecars
jobRuns to completion, no restart on success — migrations, seeding, backups
cronjobRuns to completion on a recurring schedule — requires schedule
StatefulSet
pods:
  - name: postgres
    resourceType: statefulset
    image: postgres:15
    servicePorts: [5432]

schedule

Required when resourceType: cronjob, ignored otherwise. Accepts a 5-field cron expression (0 3 * * *) or a macro: @yearly, @annually, @monthly, @weekly, @daily, @midnight, or @hourly.

Nightly backup
pods:
  - name: db-backup
    resourceType: cronjob
    schedule: "0 3 * * *"  # or "@daily"
    image: myuser/db-backup:latest

replicas

Integer ≥ 1, defaults to 1. Horizontal scaling for stateless pods only — stateful pods should stay at 1 unless the workload itself is clustering-aware. See Scaling.

pods:
  - name: api
    resourceType: deployment
    replicas: 3

resources

CPU and memory requests and limits.

Requests & limits
resources:
  requests:
    cpu: 500m        # or whole cores: "1", "2"
    memory: 512Mi    # Ki/Mi/Gi/Ti or K/M/G/T
  limits:
    cpu: 2
    memory: 1Gi

Validation error: a limit set below its matching request is rejected, not just warned.

subdomain

DNS label(s) Nexlayer prepends to your deployment's domain to route to this pod. Requires application.url (a custom domain) — it has no meaning on a preview deployment. Accepts a single label or a dotted multi-label hostname.

application:
  version: 2.0
  name: my-app
  url: mysite.com

pods:
  - name: api
    subdomain: api              # → api.mysite.com
  - name: admin
    subdomain: admin.internal   # → admin.internal.mysite.com

What validation changed

  • servicePorts is optional for job and cronjob pods — still required for every other pod.
  • At least one pod must have a path, except when every pod in the manifest is job/cronjob (pure batch/cron manifests need no path).
  • subdomain without application.url is a validation error.
  • application.version must be a number or numeric string (2, 2.0, "2.0") — anything else is rejected.
  • Using any v2 field (resourceType, replicas, resources, subdomain, schedule) without version: 2.0 set produces a warning, not an error — the field is silently ignored.

Automatic upgrade: any pod that includes a volumes entry is automatically upgraded to resourceType: statefulset, whether or not you set it explicitly — durable storage needs a stable pod identity.

Example Configurations

Simple Website

A static website or simple web app — the simplest possible deployment.

application:
  name: my-website
  url: www.mysite.com  # Remove for preview deployment

pods:
  - name: web
    image: nginx:latest
    path: /
    servicePorts: [80]

PERN Stack

PostgreSQL + Express + React + Node.js — a full-stack app with database, API, and frontend.

application:
  name: pern-app

pods:
  # Database
  - name: postgres
    image: postgres:15
    servicePorts: [5432]
    vars:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secretpassword
      POSTGRES_DB: myapp
    volumes:
      - name: pg-data
        size: 2Gi
        mountPath: /var/lib/postgresql/data

  # API
  - name: express
    image: myuser/express-api:latest
    path: /api
    servicePorts: [3000]
    vars:
      DATABASE_URL: postgresql://postgres:secretpassword@postgres.pod:5432/myapp

  # Frontend
  - name: react
    image: myuser/react-app:latest
    path: /
    servicePorts: [80]
    vars:
      API_URL: http://express.pod:3000

Notice how services reference each other using postgres.pod and express.pod.

Next.js Full-Stack

Next.js with PostgreSQL and Drizzle ORM — modern full-stack with persistent data.

application:
  name: nextjs-app

pods:
  # Next.js Frontend + API
  - name: nextjs
    image: myuser/nextjs-app:latest
    path: /
    servicePorts: [3000]
    vars:
      DATABASE_URL: postgresql://postgres:password@postgres.pod:5432/mydb

  # PostgreSQL Database
  - name: postgres
    image: postgres:15
    servicePorts: [5432]
    vars:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - name: postgres-data
        size: 1Gi
        mountPath: /var/lib/postgresql/data

Postgres + Nightly Backup (v2.0)

A StatefulSet database, a scheduled backup CronJob, and a subdomain-routed API — using v2.0's resourceType, resources, schedule, and subdomain fields.

application:
  version: 2.0
  name: my-app
  url: mysite.com

pods:
  # Database — auto-upgraded to statefulset because of volumes,
  # set explicitly here for clarity
  - name: postgres
    resourceType: statefulset
    image: postgres:15
    servicePorts: [5432]
    resources:
      requests:
        cpu: 500m
        memory: 512Mi
      limits:
        cpu: 2
        memory: 2Gi
    vars:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secretpassword
      POSTGRES_DB: myapp
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - name: pg-data
        size: 10Gi
        mountPath: /var/lib/postgresql/data

  # Nightly backup — no servicePorts or path needed
  - name: db-backup
    resourceType: cronjob
    schedule: "@daily"
    image: myuser/db-backup:latest
    vars:
      DATABASE_URL: postgresql://postgres:secretpassword@postgres.pod:5432/myapp

  # API — routed at api.mysite.com
  - name: api
    replicas: 2
    path: /
    servicePorts: [3000]
    subdomain: api
    image: myuser/api:latest
    vars:
      DATABASE_URL: postgresql://postgres:secretpassword@postgres.pod:5432/myapp

PGDATA points Postgres at a subdirectory of the mount instead of its root — the mount root can contain reserved entries (like lost+found) that block Postgres from initializing there directly.

Service Discovery

Pods can communicate with each other using internal DNS. The format is simple:

# Format: <pod-name>.pod:<port>

# Examples:
DATABASE_URL: postgresql://user:pass@postgres.pod:5432/db
API_URL: http://express.pod:3000
REDIS_URL: redis://redis.pod:6379

Let Your AI Assistant Handle It

You don't need to memorize this. Just tell your AI assistant what you want to deploy:

"Deploy this Next.js app with a PostgreSQL database. I need the data to persist."

Your assistant will generate the YAML, help you build the Docker images, and deploy everything to Nexlayer.

Next Steps