Skip to content

Services & Provisioning ​

Services are the building blocks of your application. Each service represents a containerized workload that Nanokit orchestrates.

Property Reference ​

Core Configuration ​

PropertyTypeDescription
imagestringDocker image (e.g., node:20-slim). Optional if a runtime is used.
buildstringPath to Dockerfile or build context.
runtimestringRuntime ID (from the global runtimes block). Also used for auto-discovery.
portnumberOptional. Public port bound on the host (127.0.0.1). Omit it for internal-only services — they are still routed through the gateway by domain.
exposenumberOptional. Internal container port. When omitted, Nanokit auto-detects it from the image's EXPOSE metadata (falling back to 80). See Automatic Port Detection.
pathstringLocal directory to mount as a volume (enables hot-reload during development).
startstringCommand to run as the container entrypoint.
hoststringCustom domain(s) for this service. Multiple domains can be comma-separated.
firewallstring[]Array of public ports to expose on the VPS host firewall (e.g. ['25/tcp', '587/tcp']).
depends_onstring[]Ordered dependencies. Ensures start/stop hierarchy.
updateStrategyrecreate | rollingUpdate strategy. recreate (default) stops then starts. rolling provides zero-downtime.
instancesnumberNumber of container replicas. Set to 0 to disable the service in a given environment.

Environment Variables ​

PropertyTypeDescription
envobjectKey-value pairs injected as environment variables into the container.
secretsobjectSensitive values resolved from Vault or other providers. See Secrets.

Resources & Scaling ​

PropertyTypeDescription
cpunumber | stringCPU limit (e.g., 0.5, 2.0).
memorystringMemory limit (e.g., 512mb, 1gb).
volumesstring[]Persistent data mappings using source:target syntax.
scaleobjectAuto-scaling configuration for the service.

Scale Options ​

PropertyTypeDescription
scale.minnumberMinimum number of replicas.
scale.maxnumberMaximum number of replicas.
scale.cpu_thresholdstringCPU utilization target for scaling (e.g., "70%").

Provisioning ​

PropertyTypeDescription
provisioning.installstringOne-time install command. Inherited from the Global Runtime if omitted.

Host Mapping ​

The host property maps custom domains to a service via the integrated Caddy gateway:

yaml
services:
  web:
    runtime: node
    host: my-app.localhost     # Single domain

  api:
    runtime: node
    host: api.my-app.localhost, api.localhost   # Multiple domains (comma-separated)

In production environments, Caddy automatically obtains SSL certificates via ACME (Let's Encrypt).


Automatic Port Detection ​

Ports are optional. If you declare neither expose nor port, Nanokit reads the port directly from the image's own EXPOSE metadata and wires the gateway to it — zero configuration. A standard image such as node:20 (which exposes 3000) or postgres:16 (5432) just works:

yaml
services:
  web:
    image: my-org/web:latest   # image EXPOSEs 3000 → routed automatically
    host: my-app.localhost

The detected port is used for the injected PORT environment variable, the gateway upstream, and the container's exposed port. Resolution order:

  1. expose — explicit internal port, if set.
  2. port — explicit host-bound port, if set.
  3. Image EXPOSE — the lowest exposed TCP port the image declares.
  4. 80 — final fallback when the image exposes nothing.

When multiple TCP ports are exposed, Nanokit picks the lowest (application ports are conventionally lower than auxiliary debug/metrics ports such as 9229). Set expose explicitly to override the heuristic.

Note. Auto-detection only fills the internal port. Binding a port on the host still requires an explicit port (used for raw TCP services like databases or mail). Most HTTP services need neither — they are reached through the gateway by domain.


Declarative Firewall Configuration ​

The firewall property allows you to define public ports that should be opened on the VPS host firewall. During deployment, the Nanokit CLI automatically configures UFW on the remote machine:

yaml
services:
  mail:
    image: stalwartlabs/stalwart:latest
    port: 8080
    expose: 8080
    host: mail.my-app.com
    firewall:
      - 25/tcp    # SMTP
      - 587/tcp   # SMTP Submission
      - 465/tcp   # SMTPS
      - 993/tcp   # IMAPS

When you run nkapp deploy, the CLI scans your services, aggregates all firewall rules, and securely provisions them on the remote VPS (e.g. executing sudo ufw allow 587/tcp and reloading UFW) completely transparently.


Update Strategies ​

recreate (Default) ​

The container is stopped, removed, and recreated. Brief downtime occurs during the switch.

rolling ​

A new container is started alongside the old one. Traffic is switched after the new container is healthy. The old container is then removed. Zero downtime.

yaml
services:
  api:
    image: my-app:latest
    updateStrategy: rolling

Build & Compilation Strategies ​

Nanokit handles the transition from source code to container images using a dual-mode strategy designed for both local development and professional CI/CD pipelines.

1. Artifact-based (Pre-build) ​

If Nanokit detects pre-compiled artifacts (e.g., dist/, .next/, out/, build/) in the service path or project root, it automatically includes them in the Docker context and copies them into the image.

  • Workflow: nx build → nkapp deploy.
  • Benefit: Faster image creation as compilation happens once outside of Docker. Ideal for CI/CD environments where builds are a separate stage.

2. Source-based (In-Container Build) ​

If a build command is specified in nanokit.yml, Nanokit executes it inside the Docker container during the image creation phase.

  • Workflow: nkapp deploy (Nanokit handles compilation internally).
  • Benefit: Guarantees that the code is compiled in an environment identical to the runtime. This is crucial for services with native dependencies (C++/Rust) that must match the target architecture (e.g. linux/amd64).
yaml
services:
  api:
    runtime: node
    build: npm run build   # Executed inside the Docker build process
    start: node dist/index.js

3. Native Dockerfile (Full Control) ​

If you need complete control over the container environment, you can provide your own Dockerfile. Nanokit will bypass its automatic generation and use yours instead.

  • Workflow: build: true or build: ./Dockerfile.
  • Benefit: Support for specialized system dependencies, multi-stage builds, and custom base images.

See the dedicated Native Dockerfile page for more details.


Lifecycle & Hooks ​

Nanokit allows you to hook into different stages of a service's lifecycle for tasks like database migrations, installation of dependencies, or cleanup.

For detailed information on prestart, poststart, provisioning.install, and more, see the dedicated Lifecycle & Hooks page.


Disabling a Service ​

Set instances: 0 to completely disable a service in a specific environment:

yaml
environments:
  stage:
    services:
      hub:
        instances: 0    # Hub is not deployed to staging

Dependencies (depends_on) ​

The depends_on property defines the orchestration order of your project. Nanokit calculates a Directed Acyclic Graph (DAG) to determine which services can start in parallel and which must wait for others.

Common Patterns ​

1. Database Readiness ​

The most frequent use case is ensuring an application waits for its database to be healthy before attempting a connection.

yaml
services:
  api:
    runtime: node
    depends_on:
      - test_mongo_db

2. Provisioning Master ​

When sharing volumes (like node_modules or a shared cache) between services, you can designate one "Master" service to handle the heavy lifting (installation, compilation) by making other services depend on it.

Tip. For a more elegant approach, define this dependency at the Runtime Level instead of repeating it in every service.

3. Service Hierarchy ​

In microservice architectures, you may want to ensure that a gateway or a central authentication service is up before the rest of the stack.



Complete Example ​

yaml
services:
  web:
    runtime: node
    path: ./apps/web
    port: 3000
    host: my-app.localhost
    updateStrategy: rolling
    cpu: 1.0
    memory: 1gb
    depends_on:
      - api
    env:
      NODE_ENV: production
    start: npm run start
    hooks:
      prestart: "npx prisma migrate deploy"
    volumes:
      - ./uploads:/workspace/uploads

Tip.Atomic Install: The provisioning.install command is executed only if the shared volume's sentinel file is missing, preventing redundant installs across distributed clusters.