Migrating production infrastructure? Get up to $10K in migration credits.

Apply now
Deployment

Deploying Multi-Service Apps Without Kubernetes: Services, Workers, Jobs, and Private Networking

TL;DR

  • Keep your app as one deployment until sharing it becomes a recurring constraint, then split off one service at a time. Multiple services alone don't require Kubernetes.
  • Deploy each part as a Render service: web services for public APIs, private services for internal APIs, background workers for queues, and cron jobs for scheduled tasks, with private networking for internal communication.
  • Run database migrations before deploying new application code, keeping changes compatible with the version still serving users. Then deploy workers that handle both old and new job formats before the API.
  • Define your services, databases, and their connections in one render.yaml Blueprint, even when the application code lives in separate repositories.

Deploying a multi-service app means getting its API, background workers, and scheduled jobs running and connected. You need to decide where each component runs, how internal services communicate privately, and how to keep the parts compatible as you release updates. Kubernetes can manage the workloads, but your team also needs to assemble the tools and services around it. Managed application services let you deploy those components without taking on that platform work.

This guide explains how to design that multi-service deployment on Render without managing Kubernetes. You’ll learn how to choose services, connect internal components over a private network, and coordinate API, worker, and database updates. Let’s get started!

When should you split your application into separate services?

Keep your application together while a single deployment meets its performance and release needs. Split a component when sharing that deployment becomes a recurring constraint, such as background processing competing with the API for resources. That gives the new service a specific job to do and a concrete reason to exist.

Make the change incrementally. Extract one responsibility, define how the rest of the app will communicate with it, and check that you can operate it independently. Code that usually changes together can stay together. You can keep most of the application as a monolith while running a worker separately.

Why deploy multi-service apps without Kubernetes?

Kubernetes manages containers across a group of machines, but building a platform around it also involves choosing tools for code builds, databases, and monitoring. For a small team, that work competes with time spent shipping features. Using a managed service like Render lets you run each part of your app independently without setting up a Kubernetes cluster.

On Render, you define what each service runs and how it connects. It handles builds, deployments, and the underlying servers, with managed databases alongside your APIs, workers, and scheduled jobs. You can add services as your app grows and give each part its own resources.

Consider Kubernetes when you need custom rules for placing workloads across machines. Multiple services alone don’t require it.

Which Render services should you use for each part of your app?

Use the workload to choose the Render service or datastore:

What this part of your app does
Render service or datastore
Receives API requests from users over the internet
Web service
Receives API requests only from other services on the private network
Private service
Retrieves and processes background jobs from a queue
Background worker
Runs a task on a recurring schedule
Cron job
Runs tasks with Render managing queuing, retries, and execution
Workflows
Stores application records, such as orders and billing data
Postgres
Stores queued jobs or cached data
Key Value

Public web services

Use a public web service for an API or website that users access over the internet. Render provides HTTPS automatically. Web services can automatically add or remove running instances as demand changes, helping client-facing APIs handle increases in traffic. Store session data and uploaded files where all instances can access them, so a request does not depend on one particular instance. Move slow tasks that can run asynchronously to background workers or Workflows so the API can respond without waiting for them to finish.

Private services

Use a Render private service for an API that should receive requests only from other services on your private network. For example, keep the billing API private while the public API handles requests from customers.

Background workers

Use a Render background worker for tasks such as generating reports or processing files outside an API request. Workers run continuously without accepting incoming requests. In this example, they retrieve jobs from a queue stored in Render Key Value.

Long-running jobs can occupy all available workers, leaving urgent jobs waiting. Give urgent jobs their own queue and dedicated workers so they don't have to wait behind slower tasks.

Cron jobs

Use cron jobs for scheduled tasks, such as generating a nightly report. Each run starts, performs its task, and exits. On Render, if the previous run is still active, the next scheduled run waits until it finishes.

Workflows

Use Render Workflows when you want Render to manage task queuing, retries, and execution for long-running or multi-step work. You define tasks in code, and Render starts compute when they run and releases it when they finish. A background worker fits when you want to keep an existing queue framework and run the workers that process its jobs.

Databases and queues

Use Render Postgres to store application records, such as orders and billing data. Use Render Key Value for a job queue or shared cache. For a production job queue, use a Render Key Value instance with disk persistence enabled to preserve stored state across restarts.

How do you coordinate database, worker, and API updates?

On Render, a deployment starts new instances running the updated code. During the transition, the previous version can still be running, so database changes must work with both versions.

Run database changes before deploying new code

On Render, use a preDeployCommand to run database migrations after the build finishes and before the new application version starts. The command runs on a separate instance while the previous application version continues serving users. Therefore, keep database changes compatible with the running version.

The expand-and-contract rollout

During a deployment, the queue can contain jobs created by both the old and new application versions. If a worker receives a job format it doesn’t understand, processing can fail. Update the services in this order:

  1. Add the new database columns or tables, keeping anything the running application still uses.
  2. Deploy workers that can process both the old and new job formats.
  3. Once those workers are running, deploy the web service that creates jobs in the new format.
  4. Wait until jobs in the old format have finished before removing the code that handles them.
  5. In a later database migration, remove old columns or tables only after all running services have stopped using them.

How do you keep internal services private?

On Render, services in the same workspace and region can communicate over a private network without sending traffic across the public internet. Use private services for internal APIs that should have no public URL.

Connect your services using the internal addresses Render provides. For example, your public API can call a private billing service and connect to Render Postgres through their internal addresses.

How do you handle deployment failures?

Health checks help Render decide whether an instance is ready to receive traffic. Configure a /health endpoint on your web service to check that the application is working. During deployment, Render checks the new instances before sending them traffic. If they don't become healthy within 15 minutes, Render cancels the deployment and keeps the existing version serving users. If an instance becomes unhealthy after deployment, Render temporarily stops sending it traffic and restarts it if failures continue.

After switching traffic to the new version, Render sends the old instance a SIGTERM signal, asking it to shut down. Your application should handle this signal by finishing requests already in progress and closing database connections. If it doesn't exit within the shutdown period, Render sends SIGKILL to stop it forcibly.

Render allows 30 seconds for shutdown by default. If your application needs longer, you can configure a shutdown period of up to 300 seconds for services without an attached persistent disk.

How do you define the architecture in a Render Blueprint?

A Render Blueprint describes your application’s infrastructure in a single render.yaml file. It defines the services and databases Render should create, their deployment settings, and how they connect.

For the architecture in this guide, the Blueprint brings together the public API, private billing API, background worker, nightly report, Key Value queue, and Postgres database. The example below uses separate repositories for the application services while managing their infrastructure settings together. If your services share one repository, see monorepo deployment patterns.

Use each service’s repo field to identify its source repository. For secrets such as API keys, set sync: false and supply their values in the Render Dashboard when creating the Blueprint.

Explicit repo fields link independent codebases into one environment and support Preview Environments that provision an isolated copy of the stack per pull request.

The repository URLs are placeholders. Adapt the repositories, commands, and connection settings to your application.

The worker’s REDIS_URL references the queue’s internal connection address. Your worker code reads this environment variable to connect to the queue.

BILLING_API_HOST gives the web service the billing API’s internal hostname and port. Your application uses this address when calling the billing API over the private network.

In this example, the nightly report reads order data from Postgres using DATABASE_URL.

Budget for the services your application needs, including APIs, workers, databases, and storage. Estimate each component using your expected workload and Render’s current pricing.

How do managed Kubernetes alternatives compare for multi-service apps?

Choose a platform around the work your application does and the infrastructure your team wants to manage. For a multi-service app, consider APIs, background processing, scheduled jobs, databases, and private communication together.

Your priority
Approach and examples
Why consider it?
Run APIs, workers, scheduled jobs, and data together
Managed application platforms, including Render and DigitalOcean App Platform
Both support web services and background application components. Compare how you define and connect the complete application.
Build around Next.js pages and API routes
Framework deployment and functions, such as Vercel
Deploy the frontend and server-side request handlers together, with Postgres integrations for database access.
Build within an existing Google Cloud or AWS setup
Cloud-provider services, such as Cloud Run or Amazon ECS on Fargate
Cloud Run offers services, jobs, and worker pools. ECS supports long-running services and scheduled tasks. Select the accompanying data and networking services within your chosen cloud.
Place application instances near users in selected regions
Regional application hosting, such as Fly.io
Choose where your application runs, and plan database placement alongside it.
Run workloads in a cloud account your organization controls
Managed deployment into your account, such as Northflank
Its bring-your-own-cloud option runs workloads in your account. Northflank also offers its own managed cloud.
Prototype an app with several connected services
Application hosting with databases and private networking, such as Railway
Deploy application services and databases together, using private connections between them while testing your design.

The multi-service deployment checklist

Before deploying your multi-service app, complete these steps:

  • Move slow tasks that can run asynchronously to background workers or Workflows so the API can respond without waiting for them to finish.
  • Add up the database connections your APIs, workers, and scheduled jobs can open at the same time, counting every running instance. Keep that total below your database’s connection limit.
  • Run database migrations before deploying new code, preserving columns and tables that running services still use.
  • Define application-level HTTP health endpoints (/health) so failing deploys do not take traffic.
  • Handle SIGTERM by finishing requests already in progress and closing database connections before the shutdown period ends.
  • Keep shared settings in your Blueprint and supply secret values through the Render Dashboard so they stay out of Git.

Conclusion

Your application can grow into multiple services without your team having to build and operate a Kubernetes platform. Render handles builds, deployments, and the underlying servers, with private networking and managed databases alongside your services. You choose how the application works, and Render manages the infrastructure it runs on.

Deploy your multi-service apps on Render to focus on building your product while Render manages the infrastructure.

Start building for free on Render

Frequently asked questions


*Redis is a registered trademark of Redis Ltd. Any rights therein are reserved to Redis Ltd. Any use by Render is for referential purposes only and does not indicate any sponsorship, endorsement or affiliation between Redis and Render.