Welcome to Nanorack
Nanorack is a developer-first platform designed for running containerized services with zero infrastructure overhead.
Describe your application in a nanorack.yaml manifest. Nanorack provisions automated HTTPS, sets up private networking between services, securely delivers secrets, mounts NVMe storage, and collects your logs and metrics out of the box.
Quickstart
Deploying a service takes less than a minute. Create a nanorack.yaml manifest in your project directory and deploy using the nanorack CLI.
1. Write your manifest
project: shop
stage: prod
region: dc1
---
name: hello-world
image: nginx:latest
port: 80
cpu: 0.1
memory: 64
public:
expose: http
2. Deploy via CLI
$ nanorack login
$ nanorack deploy nanorack.yaml
Stage shop/prod [dc1]
dc1 deploy hello-world ok
done: 1 deployed, 0 unchanged, 0 stopped
3. Open your service
Every service with expose: http gets a free HTTPS subdomain automatically. Use nanorack get to find it:
$ nanorack get hello-world
hello-world status=running replicas=1/1 domain=hello-world-prod-8k3fa.nrck.dev endpoints=
Manifest Reference
A nanorack.yaml file contains a coordinate document defining where the stage lives, followed by one or more service documents separated by ---.
Coordinate Fields
| Field | Type | Description |
|---|---|---|
project |
string · required | The name of your application project. |
stage |
string · required | The target deployment environment, such as prod or staging. |
region |
string · required | Target region placement, such as region: dc1. |
Service Fields
| Field | Type | Description |
|---|---|---|
name |
string · required | The service name within your stage. Doubles as the internal DNS hostname (e.g. a service named db is reachable at db.internal). |
tier |
string · optionaldefault: app |
Compute tier: app (sandboxed container, the default) or sys (dedicated virtual machine). |
image |
string · required | The Docker image to run, from a public or private registry. The tag is resolved to an exact image on each deploy — see Image Tags & Updates. |
port |
number · requiredif public.expose set |
The container port receiving traffic. Required when public.expose is set. |
cmd |
string · optional | Override the container image's default command. |
replicas |
number · optionaldefault: 1 |
Number of instances to run (max 100). Run 2+ for zero-downtime deploys and high availability — see Deployments. |
cpu |
number · requiredunless cores set |
vCPUs to reserve per replica, in steps of 0.1 (e.g. 0.5). Minimum 0.1 (App) / 0.3 (Sys). Mutually exclusive with cores. |
cores |
number · optional | Pinned dedicated CPU cores, the alternative to shared cpu. Set one, not both. |
memory |
number · required | Memory to reserve per replica, in MB. Minimum 16 (App) / 128 (Sys). |
disk |
number · optional | Ephemeral scratch disk in GB, wiped on every redeploy. |
volume |
object · optional | A dedicated NVMe volume kept across redeploys, with size (GB) and mount (absolute path). A service with a volume runs exactly one replica. |
env |
map · optional | Plain environment variables. Use secrets for anything sensitive. |
public |
object · optional | Public exposure: expose: http (optionally with custom domain) or expose: port (a raw public port, TCP and UDP). Omit to keep private. |
Stages & Service Discovery
Projects in Nanorack are organized into Stages (e.g. prod, staging).
Services within the same stage can communicate with each other using simple internal hostnames. For example, if you run a service named db in your stage, any other service in that stage can connect directly to db.internal.
Services in different stages or different projects are isolated from one another and cannot communicate over the private network.
Deployments
Deploying is declarative: nanorack deploy converges your stage to the manifest — declared services are created or updated, and anything no longer in the file is stopped.
When a service's image or configuration changes, Nanorack performs a rolling update. Whether that update is seamless depends on your replica count:
replicas: 1— the single instance is stopped and replaced, so the service has a brief interruption (a few seconds, while the new version starts and passes its health check) on each deploy.replicas: 2or more — instances are replaced one at a time while the others keep serving traffic, so deploys are zero-downtime. If the new version fails to start, the rollout automatically halts and reverts to the previous version.
The rule of thumb: run 2 or more replicas for zero-downtime deploys. That's the same thing you want for high availability — extra replicas keep your service up if a host goes away — so a single knob covers both. A single replica is the simplest, lowest-cost option, ideal for dev/staging or anything that can tolerate a momentary blip during a deploy.
Image Tags & Updates
Your image points at a tag, like nginx:latest or myapp:v2. Each time you run nanorack deploy, Nanorack looks up the exact image that tag points to at that moment and pins your service to it. Two things follow, and together they're what you want:
- Deploying ships your newest build. Rebuild and push a new image to the same tag (for example, re-push
:latest), then runnanorack deployagain — Nanorack sees the tag now resolves to a new image and rolls it out. Re-deploying is always how you pick up a new build. - Your running service never changes on its own. Between deploys, the service keeps running the exact image it was pinned to. Restarts, scaling, and moves to another host all reuse that same image — a newer image behind the tag is not picked up until you deploy again. Your app changes only when you deploy it.
So :latest is perfectly fine to use: it's convenient, and you stay in control because updates land only on a deploy. If you'd rather lock a service to one exact, unchanging build, reference it by digest (myapp@sha256:…) — that always resolves to the same image no matter what happens to any tag.
Secrets Management & Registry Auth
Set stage secrets via CLI without putting credentials in source code:
$ nanorack secret set DB_PASSWORD "your-secret-key"
Secrets are encrypted at rest using per-project data encryption keys (AES-256-GCM) and never touch your image, manifest, or build logs. Secrets are scoped to the stage, so every service in the stage sees the same set, and prod and staging keep separate values.
Static File Delivery
At container boot, secrets arrive as a read-only JSON file bind-mounted at /secrets/secrets.json — a flat map of key-value strings that your application can read during initialization.
Dynamic IMDS API (169.254.169.254)
Workloads can also fetch secrets dynamically at runtime via the host-local Instance Metadata Service (IMDS) at http://169.254.169.254/secrets. This allows applications to pick up rotated secrets without needing a container restart.
Security Requirement (SSRF Guard): Every IMDS request MUST include the X-Nanorack-Metadata: 1 HTTP header. Requests lacking this header are rejected locally to prevent Server-Side Request Forgery (SSRF) vulnerabilities in application code from leaking secrets:
curl -H "X-Nanorack-Metadata: 1" http://169.254.169.254/secrets
Private Registry Authentication
To deploy images from private container registries (such as Docker Hub, GitHub GHCR, or AWS ECR), set your registry credentials via CLI or the web dashboard:
$ nanorack registry set ghcr.io "username" "ghp_yourtoken"
Private registry credentials are encrypted at rest and served over the host-local IMDS metadata API at /registry-auth when pulling container images. Your credentials are never embedded into container images, stored in Nomad job specifications, or exposed in version control.
Public Access & Custom Domains
Configure public internet access for your services in nanorack.yaml:
Web Ingress (HTTP): Set expose: http to route web traffic to your service over HTTPS. Every exposed service gets a free generated subdomain (like myapp-a1b2c3.nrck.dev) with HTTPS already set up — no DNS configuration needed. To serve from your own domain, add domain: app.example.com and follow the two steps below.
Direct Ports (TCP/UDP): Set expose: port for game servers, database brokers, or non-HTTP protocols. Each replica is assigned its own raw public port — reachable over both TCP and UDP — stable across redeploys; run nanorack get <service> to list the endpoints.
Bringing your own domain
Serving from a hostname you own (e.g. app.example.com) takes two DNS records. Both are shown for you, with their exact values, on the domain's setup page in the dashboard — you add them once and never touch them again.
-
Point it at Nanorack. Add a
CNAMEfrom your hostname to your region's ingress:CNAME app.example.com → ingress-dc1.nanorack.devThe ingress name is region-specific — the dashboard shows the exact one for your stage — so traffic lands directly in the datacenter your service runs in. Using a
CNAMEmeans you never have to look up or maintain an IP address. -
Let us manage your certificate. Add the second
CNAMEexactly as shown. It delegates certificate validation to us so we can issue and renew your SSL certificate for you, automatically and forever:CNAME _acme-challenge.app.example.com → <your-token>.nrck-acme.dev
Once both records resolve, your certificate is issued automatically (usually within a minute) and renews on its own — there's nothing to install and nothing to rotate.
Root domains. DNS doesn't allow a CNAME on a bare root domain (example.com with no subdomain in front). For record 1, use your DNS provider's ALIAS/ANAME option, or an A record to the IP shown in the dashboard; Cloudflare users get this automatically via CNAME flattening. Record 2 is always an ordinary CNAME and is unaffected.
Putting your own CDN or proxy in front (Cloudflare, Fastly, …). Bring whatever edge you like — point record 1 at your proxy and your proxy's origin at ingress-dc1.nanorack.dev, and leave record 2 as-is. Because we validate and renew your certificate over DNS, it keeps working no matter what sits in front of us — no origin-certificate juggling, no special rules. If you use Cloudflare's proxy (orange cloud), set your SSL/TLS mode to Full (Strict) — not Flexible, which causes a redirect loop.
Compute Tiers
Nanorack provides two compute tiers for running your workloads:
App Tier (tier: app, the default): Sandboxed containers designed for web applications, APIs, background workers, and stateless microservices. Allocations start at 0.1 vCPU and 16 MB of memory.
Sys Tier (tier: sys): A dedicated virtual machine with its own kernel, built for databases, game servers, and workloads that need full kernel control. Allocations start at 0.3 vCPU and 128 MB of memory.
Disk Storage & Volumes
All storage runs on the same local NVMe drives — there is no slower tier. The only difference between the two kinds is what happens to the data when you redeploy:
Ephemeral Disk: Set disk: 10 for temporary space (build files, caches, scratch data). It is tied to the deployment — every redeploy or reschedule starts with a clean, empty disk. Never store anything there you can't lose.
Dedicated Volume: Set a volume with size (GB) and mount (path) for storage that is kept — your data survives restarts, redeploys, and reschedules. Use it for databases and anything stateful. A service with a volume runs exactly one replica.
Logs & Metrics
Every service streams its logs and reports resource usage automatically — no agents, sidecars, or configuration required. Both commands read the stage coordinate from the manifest in your current directory, and both are also available in the dashboard.
Logs
Follow a service's live output, or query its recent history:
# Follow live output
$ nanorack logs web
# Query history: the last hour, only lines matching "error"
$ nanorack logs web --since 1h --filter error
Logs are retained for 7 days. Passing any time or filter flag switches from the live stream to a historical query:
| Flag | Description |
|---|---|
--since <dur> |
Look back a relative window, e.g. 15m, 1h, 24h. |
--start / --end |
An explicit range — a duration like -6h, or an RFC 3339 timestamp. |
-f, --filter <text> |
Return only lines matching the given term. |
-n, --limit <n> |
Cap the number of lines returned (default 100, max 1000). |
The dashboard offers the same live/historical view with a time-range selector and search box.
Metrics
Check a service's current resource usage — CPU, memory, and disk — from the terminal:
$ nanorack metrics web
$ nanorack metrics web -o json # machine-readable
The dashboard graphs the same signals (plus network throughput) per service.
Platform Status
Live operational status for the Nanorack control plane, data plane, and internal network — with 90 days of uptime history — is published on the status page.
CLI Reference
The nanorack CLI is the primary way to interact with the platform. All commands run from your project directory, where they pick up the stage coordinate from your manifest.
| Command | Description |
|---|---|
nanorack login |
Authenticate via the browser and save a token locally. |
nanorack deploy <path> |
Converge the stage to the manifest: declared services are created or updated, and services no longer in the file are stopped. Use --dry-run to preview the plan, and --purge to delete undeclared services (destroying their volumes) instead of stopping them. |
nanorack purge <service> |
Tear down a stopped service, destroying its volumes and releasing its ports. |
nanorack logs <service> |
Stream the service's live logs. Add a time or filter flag — --since <dur>, --start/--end, -f/--filter <text>, -n/--limit <n> — to query up to 7 days of history instead. |
nanorack metrics <service> |
Print the service's latest CPU, memory, and disk telemetry. Add -o json for machine-readable output. |
nanorack get <service> |
Print live runtime state: status, replica count, domain, and public endpoints. Add -o json for machine-readable output. |
nanorack secret set <KEY> <VALUE> |
Set a stage-level secret. |
nanorack registry set <REGISTRY> <USER> <PASS> |
Set a project-level private registry credential (e.g. ghcr.io). |