Skip to content

Deploying an app

This guide covers a real deployment end to end, including the choices you make along the way. For the short version, see the Quickstart.

Your application needs:

  • a unique name within the project or tenant;
  • a Git repository accessible to Mikrom, or an OCI image when direct image deployments are enabled;
  • a long-running main process that serves HTTP traffic;
  • a TCP port between 1 and 65535;
  • a lightweight HTTP health-check path, usually /;
  • enough CPU and memory for the runtime and expected traffic.

The process must listen on the microVM’s network interface, not only on localhost. In frameworks that provide this option, bind to an accessible address such as 0.0.0.0 or the runtime’s equivalent.

Mikrom builds a repository with BuildKit when it contains a Dockerfile, or with Railpack when it detects a supported stack. Railpack supports common ecosystems such as Node.js, Python/Django, PHP/Laravel, Go, Rust, Ruby/Rails, Elixir/Phoenix, and static sites.

For reproducible builds, declare the language version and pin dependencies. Use a Dockerfile when the application needs system packages, a specific base image, multiple preparation steps, or a start command automatic detection cannot infer. Keep the build context small; a .dockerignore is useful.

The final image must contain everything needed at runtime. The start command must run the HTTP server in the foreground, listen on the configured port, respond at the health-check path, and shut down cleanly on termination. Do not depend on systemd, Docker-in-Docker, or services that Mikrom does not provision inside the microVM.

The default application port is 8080. You can set another valid TCP port when creating or deploying the app. If the builder detects an exposed port in the image, that value can update the deployment port. The default health-check path is /; keep it fast and inexpensive.

Test the same contract locally before deploying:

Terminal window
PORT=8080 ./start-your-app
curl --fail http://127.0.0.1:8080/

The microVM disk is not a replacement for persistent storage. Use a Mikrom-managed volume for data that must survive a restart or redeployment, and configure the application with the volume’s mount point.

Mikrom can provision Neon-backed PostgreSQL databases when that capability is enabled. Supply connection information through the secure configuration mechanism provided by the installation and run migrations in a controlled way.

The deployment contract supports environment variables and attached volumes. The exact user-facing mechanism for injecting variables and secrets depends on the installation. Never place database credentials, API keys, tokens, or certificates in source control, the Dockerfile, image arguments, or build and runtime logs.

Applications that need separate workers, cron jobs, or multiple independent processes should split those responsibilities into separate applications or explicitly coordinated services. A standard Mikrom application is designed around one primary workload.

Your repo must be buildable one of two ways:

  1. Dockerfile at the repo root. Built with Docker. Add an EXPOSE line so the router knows which port to forward to. Your process must listen on 0.0.0.0 at that port.
  2. A stack Railpack detects. No Dockerfile needed. Railpack supports common ecosystems (Node, Python/Django, PHP/Laravel, Go, Rust, Ruby/Rails, Elixir/Phoenix, static sites). It installs dependencies, builds, and derives a start command.

Keep the build context small — a .dockerignore helps.

Terminal window
mikrom app create --name my-app --git-url https://github.com/<user>/<repo>.git

This reserves the name and the hostname my-app.<apps-domain>. Nothing is built yet.

Terminal window
mikrom app deploy --name my-app --cpu 1 --memory 512M --watch

Stages you will see with --watch:

  1. Build — Docker or Railpack produces an OCI image and pushes it to the registry.
  2. Schedule — the scheduler picks a worker.
  3. Boot — the agent starts a microVM from the image.
  4. Publish — on first success, the router adds the route and requests a TLS certificate.

Pick resources with --cpu (14) and --memory (512M, 1G, 2G, 4G). Add --hypervisor firecracker or --hypervisor cloud-hypervisor to pin one.

Terminal window
mikrom app deployments --name my-app # newest deployment + status
mikrom app logs --name my-app --follow # app-wide log stream

For a specific instance:

Terminal window
mikrom deployment list # find the job id
mikrom deployment status --app my-app --job-id <job-id>
mikrom deployment logs --app my-app --job-id <job-id>

Then open https://my-app.<apps-domain>.

If the app serves on a port the builder did not detect, update it:

Terminal window
curl -X PATCH https://<api-host>/v1/apps/my-app \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"port": 3000}'

Redeploy for it to take effect.

Terminal window
git push
mikrom app deploy --name my-app --watch

The old deployment keeps running until the new one is healthy, then traffic shifts.

Terminal window
mikrom app deployments --name my-app # copy a known-good id
mikrom app activate --app my-app --deployment-id <id>
Terminal window
mikrom deployment pause --app my-app --job-id <job-id> # suspend CPU
mikrom deployment resume --app my-app --job-id <job-id>
mikrom deployment stop --app my-app --job-id <job-id> # kill the instance

Link the app to GitHub (dashboard GitHub App flow, or the github_* fields on POST /v1/apps) and Mikrom deploys on every push. For a manual webhook, take the secret from mikrom app secret --name my-app and point a repo webhook at POST /v1/webhooks/github/my-app.

  • The repository is accessible from the builder.
  • The project has a valid Dockerfile or is compatible with Railpack.
  • Language and dependency versions are declared reproducibly.
  • The start process runs in the foreground.
  • The application listens on the configured port and an accessible interface.
  • The health-check path returns a fast, valid response.
  • Required variables and secrets are configured outside the repository.
  • Persistent data uses a volume or database rather than only the ephemeral disk.
  • CPU and memory cover the runtime and expected traffic.
  • The hostname and domain are configured if the application must be public.
  • Deployment status, logs, and source commit metadata have been checked.
Terminal window
mikrom app delete --name my-app # add --yes to skip the prompt