Implement Deno microservices architecture with service discovery and load balancing

Advanced 45 min Jun 15, 2026 676 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Build a production-ready Deno microservices architecture with Consul service discovery, HAProxy load balancing, and comprehensive monitoring using Prometheus. This tutorial covers container orchestration, health checks, and automated failover for scalable applications.

Prerequisites

  • Linux server with sudo access
  • 4GB+ RAM for running multiple services
  • Basic understanding of microservices architecture
  • Familiarity with TypeScript/JavaScript

What this solves

Modern applications need to scale horizontally by breaking into smaller services, but managing multiple Deno microservices becomes complex without proper service discovery and load balancing. This tutorial builds a production-ready architecture where Deno services automatically register themselves with Consul, HAProxy distributes traffic based on health checks, and Prometheus monitors the entire stack.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of all components.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget unzip software-properties-common
sudo dnf update -y
sudo dnf install -y curl wget unzip

Install Deno runtime

Install Deno using the official installation script, then verify the installation.

curl -fsSL https://deno.land/install.sh | sh
echo 'export DENO_INSTALL="$HOME/.deno"' >> ~/.bashrc
echo 'export PATH="$DENO_INSTALL/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
deno --version

Install Consul for service discovery

Download and install HashiCorp Consul for service registration and health checking.

CONSUL_VERSION="1.17.0"
wget https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip
unzip consul_${CONSUL_VERSION}_linux_amd64.zip
sudo mv consul /usr/local/bin/
sudo chmod +x /usr/local/bin/consul
consul version

Configure Consul server

Create Consul configuration directory and setup the main configuration file.

sudo mkdir -p /etc/consul.d /opt/consul
sudo useradd --system --home /etc/consul.d --shell /bin/false consul
sudo chown -R consul:consul /etc/consul.d /opt/consul
datacenter = "dc1"
data_dir = "/opt/consul"
log_level = "INFO"
server = true
bootstrap_expect = 1
bind_addr = "0.0.0.0"
client_addr = "0.0.0.0"
ui_config {
  enabled = true
}
connect {
  enabled = true
}
ports {
  grpc = 8502
}
acl = {
  enabled = false
  default_policy = "allow"
}

Create Consul systemd service

Setup Consul to run as a systemd service with automatic restarts.

[Unit]
Description=Consul
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul.d/consul.hcl

[Service]
Type=notify
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d/
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable consul
sudo systemctl start consul
sudo systemctl status consul

Install HAProxy load balancer

Install HAProxy for distributing traffic across Deno microservices with health checks.

sudo apt install -y haproxy
sudo dnf install -y haproxy

Configure HAProxy with Consul integration

Setup HAProxy configuration with service discovery integration and health checks.

global
    daemon
    maxconn 4096
    log stdout local0
    stats socket /var/run/haproxy.sock mode 660 level admin
    stats timeout 30s

defaults
    mode http
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    option httplog
    option dontlognull
    option redispatch
    retries 3

frontend api_gateway
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/haproxy.pem
    redirect scheme https if !{ ssl_fc }
    
    # Route based on URL path
    acl is_users_service path_beg /api/users
    acl is_orders_service path_beg /api/orders
    acl is_health path /health
    
    use_backend users_service if is_users_service
    use_backend orders_service if is_orders_service
    use_backend health_check if is_health
    default_backend api_default

backend users_service
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    # Dynamic backend discovery via Consul
    server-template users 3 _users._tcp.service.consul:8080 check resolvers consul

backend orders_service
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server-template orders 3 _orders._tcp.service.consul:8081 check resolvers consul

backend api_default
    balance roundrobin
    server default 127.0.0.1:8000 check

backend health_check
    http-request return status 200 content-type text/plain string "HAProxy healthy"

resolvers consul
    nameserver consul 127.0.0.1:8600
    accepted_payload_size 8192
    hold valid 5s

listen stats
    bind *:8404
    stats enable
    stats uri /
    stats refresh 5s
    stats admin if TRUE

Install Prometheus for monitoring

Download and install Prometheus to monitor your microservices architecture.

PROMETHEUS_VERSION="2.48.0"
wget https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz
tar xvf prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz
sudo mv prometheus-${PROMETHEUS_VERSION}.linux-amd64/prometheus /usr/local/bin/
sudo mv prometheus-${PROMETHEUS_VERSION}.linux-amd64/promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo useradd --system --home /var/lib/prometheus --shell /bin/false prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus

Configure Prometheus with service discovery

Setup Prometheus to automatically discover services registered in Consul.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "/etc/prometheus/rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'consul'
    static_configs:
      - targets: ['localhost:8500']
    metrics_path: /v1/agent/metrics
    params:
      format: ['prometheus']

  - job_name: 'haproxy'
    static_configs:
      - targets: ['localhost:8404']
    metrics_path: /stats/prometheus

  - job_name: 'consul-services'
    consul_sd_configs:
      - server: 'localhost:8500'
        services: ['users', 'orders']
    relabel_configs:
      - source_labels: [__meta_consul_service]
        target_label: job
      - source_labels: [__meta_consul_node]
        target_label: instance
      - source_labels: [__meta_consul_service_address]
        target_label: __address__
      - source_labels: [__meta_consul_service_port]
        target_label: __address__
        regex: '(.*)'
        replacement: '${1}:${__meta_consul_service_port}'

  - job_name: 'deno-services'
    consul_sd_configs:
      - server: 'localhost:8500'
        tags: ['deno', 'microservice']
    relabel_configs:
      - source_labels: [__meta_consul_service]
        target_label: service
      - source_labels: [__address__]
        target_label: __address__
        regex: '([^:]+):(\d+)'
        replacement: '${1}:${2}'
    metrics_path: '/metrics'

Create Prometheus systemd service

Setup Prometheus to run as a systemd service with proper permissions.

[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
    --config.file /etc/prometheus/prometheus.yml \
    --storage.tsdb.path /var/lib/prometheus/ \
    --web.console.templates=/etc/prometheus/consoles \
    --web.console.libraries=/etc/prometheus/console_libraries \
    --web.listen-address=0.0.0.0:9090 \
    --web.enable-lifecycle
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus

Create Deno microservice template

Create a reusable template for Deno microservices with service registration and metrics.

mkdir -p ~/deno-microservices
cd ~/deno-microservices
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

export interface ServiceConfig {
  name: string;
  port: number;
  version: string;
  consulUrl?: string;
}

export class MicroService {
  private config: ServiceConfig;
  private routes: Map

Create users microservice

Build the first microservice for user management with Consul registration.

import { MicroService } from "./service-base.ts";

const service = new MicroService({
  name: "users",
  port: 8080,
  version: "1.0.0",
});

// Mock user data
const users = new Map([
  ["1", { id: "1", name: "Alice Johnson", email: "alice@example.com" }],
  ["2", { id: "2", name: "Bob Smith", email: "bob@example.com" }],
  ["3", { id: "3", name: "Carol Wilson", email: "carol@example.com" }],
]);

// GET /api/users
service.addRoute("/api/users", async (req: Request) => {
  if (req.method !== "GET") {
    return new Response("Method Not Allowed", { status: 405 });
  }
  
  return new Response(JSON.stringify(Array.from(users.values())), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
});

// GET /api/users/:id
service.addRoute("/api/users/", async (req: Request) => {
  if (req.method !== "GET") {
    return new Response("Method Not Allowed", { status: 405 });
  }
  
  const url = new URL(req.url);
  const id = url.pathname.split("/").pop();
  
  if (!id || !users.has(id)) {
    return new Response("User not found", { status: 404 });
  }
  
  return new Response(JSON.stringify(users.get(id)), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
});

if (import.meta.main) {
  service.start();
}

Create orders microservice

Build the second microservice for order management with service discovery.

import { MicroService } from "./service-base.ts";

const service = new MicroService({
  name: "orders",
  port: 8081,
  version: "1.0.0",
});

// Mock order data
const orders = new Map([
  ["1", { id: "1", userId: "1", items: ["laptop", "mouse"], total: 1299.99, status: "shipped" }],
  ["2", { id: "2", userId: "2", items: ["phone"], total: 899.99, status: "processing" }],
  ["3", { id: "3", userId: "1", items: ["keyboard"], total: 129.99, status: "delivered" }],
]);

// GET /api/orders
service.addRoute("/api/orders", async (req: Request) => {
  if (req.method !== "GET") {
    return new Response("Method Not Allowed", { status: 405 });
  }
  
  const url = new URL(req.url);
  const userId = url.searchParams.get("userId");
  
  let result = Array.from(orders.values());
  
  if (userId) {
    result = result.filter(order => order.userId === userId);
  }
  
  return new Response(JSON.stringify(result), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
});

// GET /api/orders/:id
service.addRoute("/api/orders/", async (req: Request) => {
  if (req.method !== "GET") {
    return new Response("Method Not Allowed", { status: 405 });
  }
  
  const url = new URL(req.url);
  const id = url.pathname.split("/").pop();
  
  if (!id || !orders.has(id)) {
    return new Response("Order not found", { status: 404 });
  }
  
  return new Response(JSON.stringify(orders.get(id)), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
});

if (import.meta.main) {
  service.start();
}

Create systemd services for Deno microservices

Setup systemd services to manage the Deno microservices lifecycle with automatic restarts.

[Unit]
Description=Deno Users Microservice
After=network.target consul.service
Requires=consul.service

[Service]
Type=simple
User=deno
Group=deno
WorkingDirectory=/home/deno/microservices
ExecStart=/home/deno/.deno/bin/deno run --allow-net --allow-env users-service.ts
Restart=always
RestartSec=5
Environment=SERVICE_ADDRESS=localhost
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
[Unit]
Description=Deno Orders Microservice
After=network.target consul.service
Requires=consul.service

[Service]
Type=simple
User=deno
Group=deno
WorkingDirectory=/home/deno/microservices
ExecStart=/home/deno/.deno/bin/deno run --allow-net --allow-env orders-service.ts
Restart=always
RestartSec=5
Environment=SERVICE_ADDRESS=localhost
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Create dedicated user and setup permissions

Create a dedicated user for running Deno services securely with minimal permissions.

sudo useradd --system --home /home/deno --create-home --shell /bin/bash deno
sudo mkdir -p /home/deno/microservices
sudo cp ~/deno-microservices/* /home/deno/microservices/
sudo chown -R deno:deno /home/deno
sudo chmod 755 /home/deno/microservices
sudo chmod 644 /home/deno/microservices/*.ts
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 755 for directories and 644 for files.

Start all services

Enable and start all the services in the correct order with dependency checking.

# Start Consul first
sudo systemctl status consul

# Start HAProxy
sudo 

Automated install script

Run this to automate the entire setup

Wil je dit niet zelf beheren?

Wij beheren infrastructuur voor bedrijven die afhankelijk zijn van uptime. Volledig beheerd, met één vast aanspreekpunt dat je omgeving kent.

U krijgt één vast aanspreekpunt dat uw omgeving kent

Op kantoor in Rotterdam 15:14 · bereikbaar in een bericht, geen ticketformulier