Service

A Service is the primary unit of deployment in ModStack — a collection of containers, versioned configuration, and rules that determine how they run and how they are exposed.

What is a Service?

A Service is the primary unit of deployment in ModStack. Each Service represents a collection of containers that can be deployed as a pod — a web server, a background worker, an API, or anything else that runs in containers. Each Service has a DNS name that other Services in the same Environment can use to communicate with it. A Service can also be exposed to the internet using ingress rules.

Service vs Service Template

Think of a Service Template as the blueprint and a Service as the running instance. The Service Template is an immutable, versioned snapshot of all configuration — container image, configuration sets, secrets, ingress rules, scaling settings, and build configuration. A Service is simply the deployment of a specific version of that template.

Every time you change any part of a Service's configuration you create a new version of the Service Template. Because each version is immutable, you always have a clear record of what changed between deployments. There is also no drift between the configuration and the running Service — if a Service is running a particular version, you know exactly what the configuration is.

When you create a Service, you choose which version of the Service Template to deploy. To update a running Service, you upgrade it from its current template version to a newer one. See the Deployments & Upgrades tab for details on how upgrades are performed.

Service Template v1.0.0 (immutable) v1.1.0 (immutable) v2.0.0 (immutable) User creates deploy upgrade Service (running v2.0.0) pod pod pod

Service Template

A Service Template is an immutable, versioned snapshot that contains everything needed to run a Service — the container image, configuration sets, secrets, ingress rules, scaling settings, and build configuration. It is the single source of all configuration for a Service. The sections below describe each field of the Service Template.

Name

Every Service Template requires a DNS-compatible name with a maximum length of 60 characters. Because the name is used as the service's address on the private network, it must follow DNS naming rules:

  • Only lowercase letters (a–z), digits (0–9), and hyphens (-).
  • Must start with a letter.
  • Cannot end with a hyphen.

The service name is how services address each other on the private network. Within any Environment, all Services are allowed to communicate with each other with no firewall restrictions. No communication between Services is allowed across Environments.

See the Ingress network and Internal load balancing sections for more information on network flows.

Version

Each Service Template release is versioned using the major.minor.patch format. Every time you release a new version you can choose how to increment the version based on your organisation's versioning policy.

Ingress

A Service can be exposed to the internet using ingress rules. You configure which ports receive external traffic and how requests are routed to your container. Internet ingress is supported on ports 80 (HTTP) and 443 (HTTPS). If no mapping is defined for port 80, traffic is automatically redirected to port 443.

You can map external ingress to your own domain name by configuring a custom DNS during service deployment — for example, pointing app.mydomain.com to your Service's public endpoint.

enableIngress — A boolean flag that turns ingress on or off for the Service. When disabled, the Service is only reachable from other Services within the same Environment over the private network.

ingressConfig.rules — A list of routing rules that control how external traffic reaches your containers. Each rule has the following properties:

Property Description
path The URL path that this rule matches against — for example, /app/test.
ingressPort The external port on which this rule is active. Supported values are 80 and 443.
internalLoadBalancerPort The internal load-balanced port that receives the traffic. The ingress layer forwards packets to this port, and other Services within the same Environment also connect to the Service on this port.
matchType The matching strategy for the rule. Currently the only supported type is PathPrefix, which matches any request whose path starts with the specified path value.

Example — The following rule exposes the Service at https://<rootdomain>/auth/realms/app and forwards traffic to internal load-balanced port 80:

{
  "path": "/auth/realms/app",
  "pathType": "Prefix",
  "ingressPort": 443,
  "internalLoadBalancerPort": 80,
  "matchType": "PathPrefix"
}

Resource Limits

RAM

  • Request — The amount of RAM (in MB) allocated to the Service at startup. If the Service needs additional memory it will be allocated up to the Limit.
  • Limit — The maximum amount of RAM that can be allocated. If the Service attempts to use memory beyond this value it will be OOM-killed and restarted.

If no request is specified, the full limit is allocated. In a Dev environment, where many services typically run on resource-constrained machines with very little load, a default request of 200 MB is set automatically. This allows developers to run a large number of lightly loaded services on their developer machines.

CPU

  • Request — The minimum number of millicores (1/1000th of a CPU core) guaranteed to the Service at startup.
  • Limit — The maximum number of millicores the Service can consume. If the Service attempts to use CPU beyond this value it will be throttled.

As with RAM, if no CPU request is specified, the full limit is allocated. In Dev environments, a default request of 20 millicores is applied to allow a large number of services to share limited compute resources.

Probes

Probes are periodic health checks that the platform performs against your Service to determine whether it is starting correctly, ready to accept traffic, and still running. Three probe types are available:

Startup Probe

The startup probe checks whether your application has finished initializing. Until the startup probe succeeds, the liveness and readiness probes are disabled. This is useful for Services that have a slow startup — it prevents the liveness probe from killing the container before the application is ready.

Readiness Probe

The readiness probe determines whether the Service instance is ready to accept traffic. When the probe fails, the instance is removed from the internal load balancer and stops receiving requests from both ingress and other Services in the Environment. Once the probe succeeds again, traffic is restored.

The readiness probe is critical for safe production upgrades. During a rolling upgrade, the platform upgrades instances in batches of 25%. It waits for each batch's readiness probes to pass before proceeding to the next batch. Without a readiness probe, the platform has no way to verify that newly upgraded instances are healthy — if a bad update is pushed, it will roll through every batch unchecked and bring down the entire Service. Always configure a readiness probe in production.

Liveness Probe

The liveness probe checks whether the Service instance is still running. If the probe fails for a sustained period, the platform kills and restarts the container. This helps recover from deadlocks or other states where the process is alive but unable to make progress.

Probe Configuration Fields

Field Description
path The endpoint path to probe — for example, /healthz. The path must start with /. The probe sends a GET request to this path.
port The port on which the health endpoint is listening.
probeProtocol The protocol used to perform the probe. Supported values are HTTP and HTTPS.
periodSeconds How often (in seconds) the probe is performed. Default is 10.
timeoutSeconds Number of seconds after which the probe times out. If the Service does not respond within this window, the probe is considered failed. Default is 1.
failureThreshold Number of consecutive failures required before taking action — removing the instance from the load balancer (readiness) or restarting the container (liveness). Default is 3.

High Availability & Autoscale

When you enable high availability, multiple replicas of your Service are deployed across different availability zones. This makes your Service resilient to an AZ failure.

Replica scaling can be manual or automatic. With manual scaling you specify the exact number of replicas to deploy. With autoscaling you set a minimum and maximum replica count and ModStack scales up or down based on load. The currently supported autoscaling metric is CPU utilisation. Support for additional scaling metrics is planned for a future release.

Configuration Sets

Each Service Template supports up to 10 configuration sets. A configuration set is a file — a properties file, a YAML file, or any other format — that you specify along with the path where it should be mounted inside the container. This is your primary way of providing runtime configuration to a Service.

Secrets

Secrets work similarly to configuration sets. You can specify multiple secrets, each as a file that is mounted at a specified path inside the container. Secret values are stored encrypted and are never exposed in logs, build outputs, or the dashboard UI.

Build

ModStack features an integrated build system that generates container images for deployment from your Git repository in one click. Whenever you create a new version of a Service Template, a new build is started and a new image is generated for that version.

The build system supports multi-architecture images for both amd64 and arm64 platforms. The choice of target platforms is controlled at the Environment level — when you create an Environment, you select which platforms it needs to support, and builds for that Environment will produce images accordingly.

For example, if you are creating a developer Environment on an x64 machine, you can select only amd64 as your build target — there is no need to build for arm64. Similarly, on a Mac laptop you can select only arm64. When you create a Module, images are always multi-platform, supporting both amd64 and arm64 out of the box.

ModStack build services are currently hosted on AWS. Support for additional clouds is planned for a future release.

Build Mechanism

The currently supported build mechanism is user-supplied Dockerfiles. The Dockerfile must be part of the repository specified during Service Template creation. Support for Buildpacks as an additional build mechanism is coming shortly.

GitHub Integration

To integrate your source code with ModStack, you need to install the ModStack GitHub App on your GitHub account and grant it read access to the repositories you want to use. The GitHub App is used by ModStack to read your source code from Git when you start a build.

To set up the integration:

1

Open Integrations

Navigate to Settings → Integrations in the ModStack dashboard.

2

Install the GitHub App

Click Install GitHub App. You will be redirected to GitHub.

3

Select your account

Select the GitHub organisation or account where your repositories are hosted.

4

Grant repository access

Choose whether to grant access to all repositories or select specific repositories.

5

Complete the setup

Click Install to finish. ModStack will now be able to read source code from the selected repositories.

ModStack currently supports integration with GitHub repositories. Support for GitLab and Bitbucket is coming shortly.

Monorepo vs Multi-repo

ModStack supports both monorepo and multi-repo workflows. You can use a single repository for all your services or a separate repository for each one. If you are using a monorepo, use the Path in the repo build parameter to control which directory is used as the build context for each Service Template.

Build Parameters

The following parameters are required when configuring a build:

Parameter Description
Repo The name of your Git repository.
Branch The branch to use for the build.
Path to the Dockerfile The full path to the Dockerfile from the root of the repository. The path should start with / (which indicates the root of the repo).
Dockerfile arguments Any additional arguments to pass to your build process. For example, you might pass a build version number as an argument to your Dockerfile.
Path in the repo The directory in the repository that becomes the root of the build context. The path should start with / (which indicates the root of the repo). This is particularly important in a monorepo scenario where each service lives in a different directory.

Build Caching

The ModStack build system uses registry caching to cache the intermediate and final layers generated during your build. Caching is scoped to the Service Template level — when you build a new version of a Service Template, it leverages the existing layer cache and only rebuilds the layers that have changed, significantly speeding up subsequent builds.

Deployments & Upgrades

You can upgrade a Service from one version of a Service Template to a newer version. By default, an upgrade expects the next sequential version — you cannot skip versions. If you need to skip a particular version, set skip versions to true when triggering the upgrade.

The system allows up to five versions of a Service Template to exist at any time. To create a new version beyond this limit, delete an older version first.

Rolling Upgrade

Upgrades work in a rolling fashion, upgrading your Service instances batch by batch. The batch size is 25% of your total instances — ModStack upgrades 25% of instances, waits for all upgraded instances to be running and healthy, then proceeds to the next batch.

The default timeout for each batch is 300 seconds. Your Service must be up and its readiness probe must respond within this window, otherwise the upgrade is marked as failed.

Canary and blue-green deployment strategies are planned for a future release.

Readiness Probe

It is important to specify a readiness probe for your Service. The readiness probe is how the upgrade process verifies that newly upgraded instances are healthy and ready to serve traffic. Without a readiness probe, the upgrade process has no way to determine whether your Service is running correctly.

If you push a bad update without a readiness probe, the system has no health check to gate each batch — it will proceed through every batch, upgrade all your instances, and bring down your entire Service. Always configure a readiness probe in production to ensure that each newly deployed instance is verified as healthy before the upgrade continues.

Upgrade Failures

When an upgrade fails, your Service instances end up in a mixed state. For example, if your Service has four instances and the new version has a bug that causes it to crash, the upgrade will deploy the first batch (25% — one instance) and wait for it to become healthy. Because of the bug the new instance never comes up, and after 300 seconds the upgrade fails. At this point, the original three instances remain on the old version while the fourth is running the failing new version.

The recommended way to handle upgrade failures is to fail forward: create a new version of the Service Template with the appropriate fix and restart the upgrade pointing to this corrected version.

Handling Database Upgrades

The recommended approach for handling database changes during an upgrade is to use a schema management tool such as Liquibase and run migrations as part of your Service startup.

An important consideration with this approach is that schema changes must be backward compatible. During a rolling upgrade, both the old and new versions of the Service are running simultaneously, so your schema changes must allow the older version to continue operating. For example, do not delete columns that are still in use by the older version — wait until the entire upgrade is complete before removing unused columns.