Dockerizing Azure Service Bus Emulator and Test with Logic Apps



This content originally appeared on DEV Community and was authored by Daniel Jonathan

Run Azure Logic Apps Standard with Service Bus Emulator completely in Docker – zero Azure resources needed.

This guide covers:

  • 🐳 Complete Docker setup with memory optimization
  • 🔧 Logic Apps configuration for Service Bus
  • 📨 Queue and Topic messaging patterns
  • 🎯 SQL filters for message routing
  • ✅ Real working examples (wf4, wf5, wf6, wf7)

Why This Setup?

  • ⚡ Fast local development
  • 💸 Zero Azure costs
  • 🔌 Works offline
  • 🧪 Safe testing environment
  • 🔁 Same behavior as Azure Service Bus
  • 🐳 Everything containerized

Prerequisites

Docker Memory Configuration

Critical: Service Bus Emulator requires significant memory.

Minimum Requirements:

  • Docker Desktop: 15-16GB RAM
  • Service Bus container: 8GB
  • SQL Server: 2GB

Configure Docker Desktop:

  1. Open Docker Desktop → Settings → Resources
  2. Set Memory to 15.6GB or higher
  3. Click Apply & Restart

Without sufficient memory, the Service Bus emulator will crash with Out of memory errors (exit code 133).

Project Structure

LADevStuff2/                      # Logic App project
├── connections.json              # Service Bus connection config
├── local.settings.json           # Connection strings
├── wf4/
│   └── workflow.json             # Queue receiver
├── wf5/
│   └── workflow.json             # Topic subscriber (TypeA filter)
├── wf6/
│   └── workflow.json             # Topic subscriber (TypeB filter)
└── wf7_sb_send/
    └── workflow.json             # Message sender

DockerFiles/
├── docker-compose.yml            # Container orchestration
└── ServiceBus/
    ├── Config.json               # Queue, Topic, Filters
    └── .env                      # SQL Server credentials

Step 1: Service Bus Configuration

DockerFiles/ServiceBus/Config.json

This defines your Service Bus entities with SQL filters:

{
  "UserConfig": {
    "Namespaces": [
      {
        "Name": "sbemulatorns",
        "Queues": [
          {
            "Name": "local-orders-queue",
            "Properties": {
              "DeadLetteringOnMessageExpiration": false,
              "DefaultMessageTimeToLive": "PT1H",
              "LockDuration": "PT1M",
              "MaxDeliveryCount": 10,
              "RequiresDuplicateDetection": false,
              "RequiresSession": false
            }
          }
        ],
        "Topics": [
          {
            "Name": "local-orders-topic",
            "Properties": {
              "DefaultMessageTimeToLive": "PT1H",
              "RequiresDuplicateDetection": false
            },
            "Subscriptions": [
              {
                "Name": "local-orders-sub",
                "Properties": {
                  "DeadLetteringOnMessageExpiration": false,
                  "DefaultMessageTimeToLive": "PT1H",
                  "LockDuration": "PT1M",
                  "MaxDeliveryCount": 10,
                  "RequiresSession": false
                }
              },
              {
                "Name": "wf5-sub",
                "Properties": {
                  "DeadLetteringOnMessageExpiration": false,
                  "DefaultMessageTimeToLive": "PT1H",
                  "LockDuration": "PT1M",
                  "MaxDeliveryCount": 10,
                  "RequiresSession": false
                },
                "Rules": [
                  {
                    "Name": "TypeAFilter",
                    "Properties": {
                      "FilterType": "Sql",
                      "SqlFilter": {
                        "SqlExpression": "user.orderType = 'TypeA'"
                      }
                    }
                  }
                ]
              },
              {
                "Name": "wf6-sub",
                "Properties": {
                  "DeadLetteringOnMessageExpiration": false,
                  "DefaultMessageTimeToLive": "PT1H",
                  "LockDuration": "PT1M",
                  "MaxDeliveryCount": 10,
                  "RequiresSession": false
                },
                "Rules": [
                  {
                    "Name": "TypeBFilter",
                    "Properties": {
                      "FilterType": "Sql",
                      "SqlFilter": {
                        "SqlExpression": "user.orderType = 'TypeB'"
                      }
                    }
                  }
                ]
              }
            ]
          }
        ]
      }
    ],
    "Logging": {
      "Type": "File"
    }
  }
}

💡 SQL Filter Syntax: Use user.propertyName for custom properties in filters.

DockerFiles/ServiceBus/.env

ACCEPT_EULA=Y
MSSQL_SA_PASSWORD=StrongPassword123!

Step 2: Docker Compose Setup

DockerFiles/docker-compose.yml

services:
  # Azure Storage Emulator
  azurite:
    image: mcr.microsoft.com/azure-storage/azurite
    container_name: azurite-shared
    ports:
      - "10000:10000" # Blob
      - "10001:10001" # Queue
      - "10002:10002" # Table
    networks:
      - shared-network

  # SQL Server 2022 (Service Bus backend)
  mssql:
    container_name: mssql-shared
    image: mcr.microsoft.com/mssql/server:2022-latest
    platform: linux/amd64
    env_file:
      - ./ServiceBus/.env
    mem_limit: 2g
    memswap_limit: 2g
    networks:
      - shared-network

  # Service Bus Emulator
  servicebus-emulator:
    container_name: servicebus-emulator-shared
    platform: linux/amd64
    image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
    pull_policy: always
    depends_on:
      - mssql
    volumes:
      - ./ServiceBus/Config.json:/ServiceBus_Emulator/ConfigFiles/Config.json
    ports:
      - "5672:5672" # AMQP
      - "5300:5300" # Management
    env_file:
      - ./ServiceBus/.env
    environment:
      SQL_SERVER: mssql
      ACCEPT_EULA: Y
    mem_limit: 8g
    memswap_limit: 8g
    shm_size: 2g
    networks:
      - shared-network

  # Logic App Container
  logicapp2:
    platform: linux/amd64
    build:
      context: ../LADevStuff2
    container_name: logicapp2
    ports:
      - "7072:7072"
    environment:
      AzureWebJobsStorage: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;QueueEndpoint=http://azurite:10001/devstoreaccount1;TableEndpoint=http://azurite:10002/devstoreaccount1"
      WORKFLOWS_STORAGE_PROVIDER: "AzureStorage"
      WORKFLOWS_STORAGE_CONNECTION_STRING: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;QueueEndpoint=http://azurite:10001/devstoreaccount1;TableEndpoint=http://azurite:10002/devstoreaccount1"
      FUNCTIONS_WORKER_RUNTIME: "node"
      AzureWebJobsFeatureFlags: "EnableMultiLanguageWorker"
      AzureWebJobsSecretStorageType: "Files"
      APP_KIND: "workflowapp"
      WEBSITE_SITE_NAME: "logicapp-local2"
    extra_hosts:
      - "localhost:host-gateway"
    depends_on:
      - azurite
      - servicebus-emulator
    networks:
      - shared-network

networks:
  shared-network:
    driver: bridge

Key Configuration Notes:

  • mem_limit: 8g – Critical for Service Bus stability
  • platform: linux/amd64 – Required for Apple Silicon (ARM) Macs
  • extra_hosts: localhost:host-gateway – Enables Logic Apps to reach Service Bus emulator
  • MSSQL Server 2022 – Use this, not SQL Edge (SQL Edge causes crashes)

Step 3: Logic App Configuration

3.1 Connection String Setup

LADevStuff2/local.settings.json

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "servicebusconnection": "Endpoint=sb://localhost:5672;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;"
  }
}

🔑 Important: Use localhost:5672, not the container name, due to extra_hosts configuration.

3.2 Service Bus Connection

LADevStuff2/connections.json

⚠ Critical: You cannot create Service Bus connections in the Logic App Designer for queues and topics.

Workaround: Manually edit this file:

{
  "managedApiConnections": {},
  "serviceProviderConnections": {
    "serviceBus": {
      "parameterValues": {
        "connectionString": "@appsetting('servicebusconnection')"
      },
      "parameterSetName": "connectionString",
      "serviceProvider": {
        "id": "/serviceProviders/serviceBus"
      },
      "displayName": "sbConnection"
    }
  }
}

Then specify queue/topic/subscription names as custom values in your workflow triggers.

Step 4: Workflow Examples

4.1 Message Sender (wf7_sb_send)

LADevStuff2/wf7_sb_send/workflow.json

💡 Key Point: userProperties.orderType is used by SQL filters to route messages.

4.2 SB Receivers (wf4,wf5,wf6)

LADevStuff2/wf4/workflow.json

📝 Note: queueName, topicName, and subscriptionName are specified as custom values in the trigger, not selected from a dropdown.

🎯 wf5 : Receives only messages where user.orderType = 'TypeA' (defined in Config.json)

🎯 wf6 : Receives only messages where user.orderType = 'TypeB' (defined in Config.json)

Step 5: Start Everything

cd DockerFiles
docker compose up -d

Verify Service Bus is running:

docker logs servicebus-emulator-shared --tail 20

Look for:

info: Emulator Service is Successfully Up!
info: Creating queue: local-orders-queue
info: Creating topic: local-orders-topic
info: Entity Sync complete; Operation Result:True

Check all containers:

docker compose ps

All should show status “Up”.

Step 6: Test the Workflows

Send TypeA Order

Making the HTTP Request

Use Postman (or curl) to send a POST request to the wf7_sb_send workflow:

What happens:

  1. HTTP POST triggers wf7_sb_send with payload {"OrderType": "TypeA"}
  2. The workflow sends the message to:
    • Queue: local-orders-queue (no filtering)
    • Topic: local-orders-topic with userProperties.orderType = “TypeA”
  3. The userProperties field is what SQL filters use for message routing

Workflow Execution

RunHistory Viewer shows the send workflow successfully completed:

Expected behavior:

  • ✅ wf4 processes message from queue (all messages go here)
  • ✅ wf5 processes message from topic (TypeA filter matches)
  • ❌ wf6 does NOT process (TypeB filter doesn’t match)

Send TypeB Order

Follow the same process as TypeA, but change the payload to:

{ "OrderType": "TypeB" }

Expected behavior:

  • ✅ wf4 processes message from queue (receives all messages)
  • ❌ wf5 does NOT process (TypeA filter doesn’t match)
  • ✅ wf6 processes message from topic (TypeB filter matches)

The SQL filters ensure each workflow only receives messages matching its subscription criteria.

Step 7: Verify Execution

Use the RunHistory Viewer in VS Code to verify workflow executions:

Topic Subscriber (wf5) – TypeA Filter

The screenshot shows the workflow successfully received and processed messages filtered by user.orderType = 'TypeA'.

Queue Receiver (wf4) – All Messages

The screenshot shows the workflow processing all messages from the queue, regardless of message properties.

Key Takeaways

✅ Memory is critical – 15.6GB Docker, 8GB Service Bus, 2GB MSSQL
✅ Use MSSQL Server 2022 – Not SQL Edge
✅ extra_hosts configuration – Required for Docker networking
✅ SQL filters use user. prefix – For custom properties
✅ Manually edit connections.json – Designer doesn’t support queue/topic setup
✅ localhost:5672 connection string – Due to extra_hosts configuration
✅ Rebuild containers – After workflow changes

Summary

You now have a complete local development environment with:

✅ Service Bus Emulator running in Docker
✅ Queue for point-to-point messaging
✅ Topic with SQL-filtered subscriptions for pub/sub
✅ Multiple Logic Apps in containers
✅ Proper memory configuration for stability
✅ Working examples (wf4, wf5, wf6, wf7)

This setup is perfect for:

  • Local development and testing
  • Learning Logic Apps and Service Bus patterns
  • Integration demos
  • Offline development

You can later swap the emulator connection string for Azure Service Bus without changing your workflows!

Happy integrating! 🚀🐳


This content originally appeared on DEV Community and was authored by Daniel Jonathan