Continuous Profiling

Continuous profiling captures real-time performance data from your applications - CPU usage, memory allocations, wall-clock time, and more. Moneat ingests profiles from both the Sentry SDK and the Datadog Agent, so you can use whichever fits your stack.

Enable profiling with the Datadog Agent

datadog.yaml is the main configuration file for the Datadog Agent. Its default location depends on your platform:

PlatformPath
Linux/etc/datadog-agent/datadog.yaml
macOS/opt/datadog-agent/etc/datadog.yaml
WindowsC:\ProgramData\Datadog\datadog.yaml
DockerPass configuration via environment variables (see below)

Add the following to your datadog.yaml:

YAML
# datadog.yaml
apm_config:
  enabled: true
  profiling_dd_url: "__MONEAT_BACKEND_URL__/api/v2/profile?api_key=<YOUR_MONEAT_AGENT_KEY>"
  telemetry:
    dd_url: "__MONEAT_BACKEND_URL__/dd/telemetry/proxy"

# Point to your Moneat instance
dd_url: "__MONEAT_BACKEND_URL__/dd"
Docker

If you're running the Agent in Docker, pass the profiling endpoint as an environment variable. The EPForwarder redirects require a datadog.yaml file (see Agent Setup):

Shell
docker run -e DD_APM_ENABLED=true \
          -e DD_DD_URL=__MONEAT_BACKEND_URL__/dd \
          -e DD_APM_CONFIG_PROFILING_DD_URL=__MONEAT_BACKEND_URL__/api/v2/profile?api_key=<YOUR_MONEAT_AGENT_KEY> \
           datadog/agent:latest

Then restart the Datadog Agent:

Shell
# Linux / macOS (systemd)
sudo systemctl restart datadog-agent

# Docker
docker restart dd-agent

Once the Agent is running, instrument your applications as described below.

How profiling works

Configuring datadog.yaml tells the Agent to accept and forward profiles - it does not automatically profile every process on the host. Each application you want to profile must be individually instrumented with the Datadog language-specific profiler, which handles the actual data collection and sends it to the Agent.

The Agent then forwards those profiles to Moneat.

Instrumenting your application

Each profiler sends data to the Agent at localhost:8126 by default. In Docker environments, set DD_AGENT_HOST to the Agent container's hostname instead.

DD_SERVICE controls how your app appears in the Profiles dashboard - use something descriptive like payments-service or user-api.

Java

Standalone:

Download the Datadog Java agent JAR:

Shell
wget -O dd-java-agent.jar 'https://dtdg.co/latest-java-tracer'

Attach it at JVM startup:

Shell
DD_SERVICE=my-java-app \
DD_ENV=production \
DD_PROFILING_ENABLED=true \
java -javaagent:/path/to/dd-java-agent.jar -jar my-app.jar

Docker:

Add the agent JAR to your image and set DD_AGENT_HOST to reach the Agent container:

Dockerfile
FROM eclipse-temurin:21-jre

RUN apt-get update && apt-get install -y wget && \
    wget -O /dd-java-agent.jar 'https://dtdg.co/latest-java-tracer'

COPY target/my-app.jar /app.jar

ENTRYPOINT ["java", "-javaagent:/dd-java-agent.jar", "-jar", "/app.jar"]

Then in your docker-compose.yaml:

YAML
services:
  datadog-agent:
    image: datadog/agent:latest
    environment:
     - DD_APM_ENABLED=true
     - DD_APM_NON_LOCAL_TRAFFIC=true
     - DD_DD_URL=__MONEAT_BACKEND_URL__/dd
     - DD_APM_CONFIG_PROFILING_DD_URL=__MONEAT_BACKEND_URL__/api/v2/profile?api_key=<YOUR_MONEAT_AGENT_KEY>
     - DD_API_KEY=<YOUR_MONEAT_AGENT_KEY>
    volumes:
     - /var/run/docker.sock:/var/run/docker.sock:ro
     - /proc/:/host/proc/:ro
     - /sys/fs/cgroup/:/host/sys/fs/cgroup:ro
    pid: host

  my-java-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-java-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true
      # DD_API_KEY must be a 32-character hex string for the Java profiler's
      # format check. It is only validated locally - the agent uses its own key.
     - DD_API_KEY=00000000000000000000000000000000
    depends_on:
     - datadog-agent

DD_AGENT_HOST=datadog-agent tells the Java profiler to send profiles to the Agent container rather than localhost.


Python

Standalone:

Shell
pip install ddtrace

DD_SERVICE=my-python-app \
DD_ENV=production \
DD_PROFILING_ENABLED=true \
ddtrace-run python my_app.py

Docker:

Dockerfile
FROM python:3.12-slim

RUN pip install ddtrace
COPY . /app
WORKDIR /app

CMD ["ddtrace-run", "python", "my_app.py"]
YAML
# docker-compose.yaml
services:
  my-python-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-python-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true

Node.js

Standalone:

Shell
npm install dd-trace

Initialize at the very top of your entry file, before any other imports:

JavaScript
// Must be the first line
require('dd-trace').init({
  service: 'my-node-app',
  env: 'production',
  profiling: true,
});

Docker:

Dockerfile
FROM node:20-slim

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

CMD ["node", "server.js"]
YAML
# docker-compose.yaml
services:
  my-node-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-node-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true
React and other browser apps

React runs in the browser, not on a server, so the Datadog Agent cannot profile it - there is no process for the Agent to attach to. If you have a Node.js backend serving your React app, you can profile that using the setup above.

For profiling browser-side JavaScript (React, Vue, Angular, etc.), use the Sentry SDK instead - see the Profiling with the Sentry SDK section below.


Go

Add the profiler to your application:

Shell
go get gopkg.in/DataDog/dd-trace-go.v1/profiler
Go
package main

import (
    "log"
    "time"

    "gopkg.in/DataDog/dd-trace-go.v1/profiler"
)

func main() {
    err := profiler.Start(
        profiler.WithService("my-go-app"),
        profiler.WithEnv("production"),
        profiler.WithProfileTypes(
            profiler.CPUProfile,
            profiler.HeapProfile,
            profiler.GoroutineProfile,
        ),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer profiler.Stop()

    // your application code
}

Docker: Set DD_AGENT_HOST in your container environment (no JAR or binary download needed - the profiler is linked into your binary):

YAML
# docker-compose.yaml
services:
  my-go-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-go-app
     - DD_ENV=production

Ruby

Add the gem to your Gemfile:

Ruby
gem 'datadog'

Then enable profiling in your app initializer:

Ruby
require 'datadog/profiling/preload'

Datadog.configure do |c|
  c.service = 'my-ruby-app'
  c.env = 'production'
  c.profiling.enabled = true
end

Docker:

Dockerfile
FROM ruby:3.3-slim

WORKDIR /app
COPY Gemfile* ./
RUN bundle install
COPY . .

CMD ["ruby", "app.rb"]
YAML
# docker-compose.yaml
services:
  my-ruby-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-ruby-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true

.NET

Install the Datadog .NET tracer. On Linux:

Shell
wget https://github.com/DataDog/dd-trace-dotnet/releases/latest/download/datadog-dotnet-apm_amd64.deb
dpkg -i datadog-dotnet-apm_amd64.deb
/opt/datadog/createLogPath.sh

Set environment variables before running your app:

Shell
export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER={846F5F1C-F9AE-4B07-969E-05C26BC060D8}
export CORECLR_PROFILER_PATH=/opt/datadog/Datadog.Trace.ClrProfiler.Native.so
export DD_DOTNET_TRACER_HOME=/opt/datadog
export DD_SERVICE=my-dotnet-app
export DD_ENV=production
export DD_PROFILING_ENABLED=true

dotnet my-app.dll

Docker:

Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0

RUN apt-get update && apt-get install -y wget && \
    wget https://github.com/DataDog/dd-trace-dotnet/releases/latest/download/datadog-dotnet-apm_amd64.deb && \
    dpkg -i datadog-dotnet-apm_amd64.deb && \
    /opt/datadog/createLogPath.sh

COPY --from=build /app/publish /app
WORKDIR /app

ENV CORECLR_ENABLE_PROFILING=1
ENV CORECLR_PROFILER={846F5F1C-F9AE-4B07-969E-05C26BC060D8}
ENV CORECLR_PROFILER_PATH=/opt/datadog/Datadog.Trace.ClrProfiler.Native.so
ENV DD_DOTNET_TRACER_HOME=/opt/datadog

ENTRYPOINT ["dotnet", "my-app.dll"]
YAML
# docker-compose.yaml
services:
  my-dotnet-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-dotnet-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true

PHP

Install the Datadog PHP tracer extension:

Shell
wget https://github.com/DataDog/dd-trace-php/releases/latest/download/datadog-setup.php
php datadog-setup.php --php-bin=all --enable-profiling

Set the environment before starting PHP-FPM or your web server:

Shell
DD_SERVICE=my-php-app \
DD_ENV=production \
DD_PROFILING_ENABLED=true \
php-fpm

Docker:

Dockerfile
FROM php:8.3-fpm

RUN apt-get update && apt-get install -y wget && \
    wget https://github.com/DataDog/dd-trace-php/releases/latest/download/datadog-setup.php && \
    php datadog-setup.php --php-bin=all --enable-profiling

CMD ["php-fpm"]
YAML
# docker-compose.yaml
services:
  my-php-app:
    build: .
    environment:
     - DD_AGENT_HOST=datadog-agent
     - DD_SERVICE=my-php-app
     - DD_ENV=production
     - DD_PROFILING_ENABLED=true

Supported profile types

Moneat supports the following profile types from the Datadog Agent:

Profile TypeDescription
cpuCPU time consumed by your application
wallWall-clock time (real elapsed time)
heapCurrent heap memory usage
allocMemory allocation rate
goroutineActive goroutines (Go applications)
mutexMutex contention and wait times
blockBlocking operations and wait times
The available profile types depend on your application's language and runtime. Go applications support all types listed above, while other languages may support a subset.

Viewing profiles

Navigate to Profiles in the dashboard and select the Datadog Agent tab.

Filtering

  • Service - Search by service name to focus on a specific application
  • Type - Filter by profile type (CPU, heap, wall, etc.)

Profile details

Each profile entry shows:

  • Service - The application or service that generated the profile
  • Type - The kind of profile (CPU, heap, wall, etc.)
  • Environment - The deployment environment (production, staging)
  • Host - The host where the profile was collected
  • Duration - How long the profiling session lasted
  • Size - The profile data size
  • Time - When the profile was captured

Click on any profile to view its detailed flamegraph and drill into specific functions.

Profiling with the Sentry SDK

You can also collect profiles using the Sentry SDK. For Node.js, use @sentry/node and the matching @sentry/profiling-node package at version 10.70.0 or later, with both packages at exactly the same version. Configure profileSessionSampleRate and profileLifecycle: "trace". See the Profiles → Sentry SDK tab in the dashboard for platform-specific setup instructions.

JavaScript / TypeScript:

JavaScript
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from "@sentry/profiling-node";

Sentry.init({
  dsn: "YOUR_DSN_HERE",
  integrations: [nodeProfilingIntegration()],
  tracesSampleRate: 1.0,
  profileSessionSampleRate: 1.0,
  profileLifecycle: "trace",
});

Python:

Python
import sentry_sdk

sentry_sdk.init(
    dsn="YOUR_DSN_HERE",
    traces_sample_rate=1.0,
    profiles_sample_rate=1.0,
)
Profiles from both sources (Sentry SDK and Datadog Agent) are stored and queried separately. Use the tabs on the Profiles page to switch between them.