Skip to content
Veltic

Runtimes

Every language an application or a function can be written in, and how each one is built.

View as Markdown

Every runtime in the catalog

A Veltic application is a container that listens on 0.0.0.0 and on the PORT it is given. The runtime decides what runs inside it, and 13 of them ship with the platform. This list comes from the same catalog the server validates against, so it cannot promise something a deployment would reject.

  • NestJS (nestjs): applications only, shipped ready to run.
  • Next.js (nextjs): applications only, shipped ready to run.
  • Node.js (custom): applications and functions, shipped ready to run.
  • Python (python): applications and functions, dependencies installed when the container starts.
  • Go (go): applications and functions, compiled in an isolated build container.
  • ASP.NET Core (dotnet): applications and functions, compiled in an isolated build container.
  • Rust (rust): applications and functions, compiled in an isolated build container.
  • Java (java): applications and functions, compiled in an isolated build container.
  • Bun (bun): applications and functions, compiled in an isolated build container.
  • Deno (deno): applications and functions, compiled in an isolated build container.
  • PHP (php): applications only, compiled in an isolated build container.
  • Ruby (ruby): applications only, compiled in an isolated build container.
  • Docker (docker): applications and functions, built into a container image.

Pick one or let the CLI pick

Without --runtime the CLI infers it from the files in the directory: go.mod means Go, Cargo.toml means Rust, a Dockerfile means Docker. Aliases such as golang, csharp, aspnetcore or node are accepted wherever a runtime id is.

bashFour applications, four languages
veltic apps deploy PROJECT_ID storefront ./ --runtime nextjs
veltic apps deploy PROJECT_ID orders-api ./ --runtime go
veltic apps deploy PROJECT_ID billing-api ./ --runtime dotnet
veltic apps deploy PROJECT_ID legacy-api ./ --runtime docker

The four build strategies

How a runtime reaches a running container differs, and the difference is worth knowing because it decides how long your first deployment takes.

  • Shipped ready to run

    Node applications upload their finished build and their node_modules. Nothing happens on the server beyond starting the container, which makes this the fastest path.

  • Installed at start

    Python installs requirements.txt when the container starts. It is the one runtime that keeps a writable filesystem, and the one whose first start takes noticeably longer.

  • Compiled in a build container

    Go, .NET, Rust, Java, Bun, Deno, PHP and Ruby are compiled before the application starts, in a throwaway container with capabilities dropped and a hard memory limit. Dependency caches survive deployments, so the second build is markedly faster than the first.

  • Built as an image

    Docker builds the Dockerfile from the root of your deployment and runs the result. The image is swapped on the next successful deployment and removed when the application is deleted.

What every runtime must do

The rules are the same in all thirteen, because they come from the container rather than from the language.

  • Listen on 0.0.0.0, not on 127.0.0.1. Inside the container loopback is only the container itself, so the published port would stay silent.
  • Read PORT from the environment. Veltic assigns it; an application that hardcodes one is unreachable.
  • Leave the deployment directory alone. It is mounted read-only at runtime, and only /tmp is writable.
  • Expect the platform variables to win. ASPNETCORE_URLS, DENO_DIR and their siblings override a variable of the same name, so no configuration can break the start of its own application.
  • Answer an HTTP request after starting. The probe tries /api/health, /health, /api/v1/health and /, and treats anything below 500 as healthy.

Functions take the same runtimes

A function is the same container without a port and without an Nginx entry, so it runs on the same catalog: custom, python, go, dotnet, rust, java, bun, deno, docker. Only the hand-over differs. The default runtime takes a single entry file and bundles it with esbuild; every other runtime takes a directory, packages it and builds it on the server. Web server runtimes are not offered for functions, because a worker is not a request handler.

bashA bundled file, and a packaged directory
veltic functions deploy PROJECT_ID reconcile ./worker.ts --memory 256
veltic functions deploy PROJECT_ID importer ./ --runtime go --memory 512

Docker is the escape hatch

If your language is not in the catalog, or you need a multi-stage build, system packages or an unusual base image, ship a Dockerfile. It inherits the same isolation as every other application, with one deliberate exception: the filesystem stays writable, because a foreign image usually needs it. Capabilities are still dropped, privileges cannot be gained, the process and memory limits still apply, and the port is still published on loopback only.

bashAny language at all
veltic apps deploy PROJECT_ID legacy-api ./ --runtime docker
veltic apps logs PROJECT_ID legacy-api --follow

Where the build output goes

A compiled runtime writes its artefact into the release directory and the application starts from there. The output of that build is not part of the log stream, because the container it came from no longer exists by the time the application runs: it is returned with the deployment and printed by the CLI. Live logs, from then on, belong to the running container and are identical for every runtime.