Skip to content

Services & builds

A service is one long-running workload in a project: a web app, an API, a worker. It runs from a container image, either one you name (nginx:1.27, ghcr.io/you/app:v3) or one Meshploy builds: from your git repository, or from a folder on your computer.

A service has its own variables, ports, replicas and resource limits. Its page shows what it runs now and where that came from, its deployments, its pods and their logs, and a terminal into a running pod.

Meshploy clones the branch you choose and builds an image with one of two builders:

  • Railpack looks at the code and works out how to build it, with no Dockerfile needed.
  • Dockerfile builds your own Dockerfile, from the path you give.

A build runs as a job on a build node, pushes the image to your registry (the built-in one, or a registry you connected), then deploys it. The build log streams on the deployment’s page, and the deployment records the branch and commit it built.

A service can be built from a folder instead of a repository: an app that is not in git yet, or one an agent just wrote. Choose Folder as its source and drop the folder on the page, or run meshploy deploy ./my-app from a terminal. Either way the folder is packed where it is, sent, and built as a clone would be, with the same builders.

Some things are never sent: .git, installed dependencies such as node_modules (the build installs its own), and .env files, which hold secrets (an .env.example is kept). Neither is anything the folder’s .meshployignore names, or what a .gitignore names in the folder it sits in, at any depth. Up to 100 MB can be sent once packed.

A new version replaces the last one: drop the folder again on the service’s Configuration page, or run the command again. Rollback still goes back to earlier images. A deployment built from a folder says Built from a folder, and where a git build records its commit it records the first characters of the folder’s fingerprint. Choosing a repository later builds from the repository instead.

Three optional commands. Left empty, each is worked out for you, as before:

  • Install command (pnpm install --frozen-lockfile) and build command (pnpm build) replace the steps Railpack works out from the code. A Dockerfile build has its own steps, so they apply to Railpack only.
  • Start command (node dist/server.js) replaces the command the image starts with, whether Railpack built it, a Dockerfile did, or it is an image you named. It runs through the image’s shell, so && and pipes work, and it changes on the next deploy with no rebuild. An image without a shell, such as a distroless one, cannot use it.

A project keeps a build cache: layers from earlier builds, so the next build reuses what has not changed and is faster. It is shared by every service in the project, and after each build it is trimmed back under the server’s limit (5 GB unless the server sets BUILD_CACHE_LIMIT_GB). A Railpack build drops the layers used least recently, so what the next build needs stays; a Dockerfile build over the limit starts its cache again. Nothing fails at the limit: a build is only slower when it cannot reuse a layer.

Clear build cache, in the project’s settings or any of its services’, empties it at once: to free the space, or to force a clean build.

With auto-deploy on, a push to the tracked branch builds and deploys by itself. A GitHub connection reports every push through the Meshploy GitHub App; GitLab, Gitea and Bitbucket use a webhook on the repository, which Meshploy adds when its token allows. Watch paths narrow it to pushes that change certain folders, for a repository that holds more than one service.

The deploy webhook URL builds the service whenever it is called, from a CI job, a cron, or a public repository with no connection. The token in it is the only thing guarding it: treat it as a credential.

A service’s own variables are set on its Configuration tab. It can also attach variable groups: shared ones, and the ones other services publish, such as a database’s connection details. Its own variables win on a clash.

A value can refer to another with ${NAME}, so DATABASE_URL=${PRIMARY_DB_URL} reads the database’s published URL instead of a copy that goes stale. A reference to a name the service does not have is left as written and noted in the deploy’s log. Build variables are a separate block, given to the builder only.

  • Deploy builds from git or the last folder sent, or deploys the configured image.
  • Rollback runs an earlier deployment’s image again, as it was, from the Deployments tab. Any deployment whose image is still kept can be rolled back to; one whose image was removed says image removed.
  • Redeploy runs the current image again, for changed variables, and builds nothing.

A deploy fails at once when no online node can build, and after a few minutes when no node can take the service. It succeeds only once the new pods have stayed up for 10 seconds without restarting, so an app that starts and exits a moment later, on a missing variable say, fails the deploy with its own exit code rather than passing it.

A service made on its own keeps the images of its last 3 deployments, set under Settings, Rollback: older ones are removed after each build, and when the limit is saved. An image another level runs is always kept, so a promotion never loses what it moved. Turned off, a service keeps every image it builds and the registry grows with each one. Services made before this was the default keep every image, and their level’s Services page offers to move them to the last 3. A stack’s services keep images as the stack says.

Removing an image frees its layers only once the built-in registry is garbage-collected, which the gateway does by itself at most weekly, when images were removed and no build is pushing.

A service whose container keeps dying can still say “running”, because it is restarted each time. Meshploy reads why from its pods and says so on the service’s page, on its card, in the services list and on the workspace overview:

  • Out of memory: it went over its memory limit and was killed. Raise the limit on its Configuration page, under resources, or find what uses more than expected. The default of 512 MB is too little for many apps that load a model or a large dataset.
  • Keeps stopping: its process exits and is restarted, with the exit code. Its log says why.
  • Cannot pull its image, or cannot start: the image name, the registry’s credentials, or its configuration needs fixing.

Restarts from more than half an hour ago are not counted, so a service that recovered stops being flagged.

While it waits to restart, its Logs tab shows the run that exited, with its exit code, since the run about to start has written nothing yet. Stopping a service removes its pod and the pod’s logs, so Stop keeps the last 500 lines it wrote, and the Logs tab shows them, with when it stopped, until it runs again.

Choosing a repository and branch for a new service has Meshploy look at a few of its files first: the dependency files, the usual entry points and a Dockerfile. It says what it found under the repository, and fills in what fits:

  • Builder: the repository’s Dockerfile when it says how to start the app, Railpack otherwise.
  • Port: from the Dockerfile’s EXPOSE, or the framework’s usual one.
  • Start command: for a FastAPI, Flask, Django or Node.js app Railpack may not know how to start. Check it before deploying.
  • Memory limit: more than the default when the app depends on something that needs it, such as PyTorch.

Only fields still holding their default are filled in, so nothing typed by hand is replaced. Every value stays editable, and the suggestions after the first build catch what a look at a few files cannot see.

Each build also looks at what the app is: its language, its framework and entry point, dependencies known to need a lot of memory, a Dockerfile in the repository. Set against the service’s settings, that turns into suggestions on the service’s page, each with its fix, and a line on the workspace overview:

  • No start command: Railpack could not tell how to start the app, so its container has nothing to run. For a FastAPI, Flask, Django or Node.js app Meshploy suggests one, such as uvicorn app.main:app --host 0.0.0.0 --port $PORT, to check before saving. When the repository has a Dockerfile that starts it, building with that is offered too.
  • Likely to need more memory: it depends on something like PyTorch, sentence-transformers or Puppeteer, which usually needs more than its memory limit.
  • Listens on a different port: the start command, or the Dockerfile’s EXPOSE, names a port other than the one the service sends traffic to.

A fix saves the setting; it reaches the running app on the next deploy, which the page offers. A suggestion goes away once the settings no longer match it, or when someone dismisses it, which sets it aside for everyone.

  • Deploy from a repository. New resource, choose Service, pick the git connection, repository and branch, then Deploy.
  • Deploy a folder from your computer. New resource, choose Service, choose Folder and drop the folder in, or run meshploy deploy ./my-app.
  • Connect a database. Attach the database’s variable group on the service’s Configuration tab, then use ${<NAME>_URL} in a variable, and redeploy.
  • Go back to the last good version. Deployments tab, choose an earlier successful deployment, Rollback.

  • Folder source: A service built from a folder sent from your computer, not a repository. Sent again, it replaces the last one. More
  • Build node: A node that runs image builds. The gateway is one by default; any node can be made one. More
  • Auto-deploy: Build and deploy on every push to the tracked branch. More
  • Watch paths: Only pushes that change these folders deploy; for a repository holding more than one service. More
  • Deploy webhook: A URL that builds this service whenever it is called. Its token is the only guard: treat it as a credential. More
  • Install command: Replaces the install step Railpack works out, such as pnpm install. Empty: Railpack decides. More
  • Build command: Replaces the build step Railpack works out, such as pnpm build. Empty: Railpack decides. More
  • Start command: Replaces the command the image starts with, run through its shell; changes on the next deploy without a rebuild. Empty: the image’s own. More
  • Out of memory: The container went over its memory limit and was killed. Raise the limit under the service’s resources. More
  • Detected: What a look at the repository found, and the settings it filled in. Every one stays editable. More
  • Suggestion: Advice from the last build about how the service is set up, such as a missing start command or too little memory for what the app loads, with its fix. More
  • Build cache: Layers kept from earlier builds, shared by the project’s services and trimmed back to the server’s limit after each build. Clearing it only makes the next build slower. More
  • Redeploy: Runs the image the service runs now again, for changed variables, without building. More
  • Rollback: Runs an earlier deployment’s image again, as it was. More
  • Image retention: How many built images a service keeps, 3 unless changed, which is how far back a rollback can go. Off keeps every image. More