Adding Custom Services
better-openclaw is designed to be extensible. You can add your own service definitions to the catalog, either for personal use or to contribute back to the project. This guide explains the ServiceDefinition interface and how to create a new service.
Where Services Live
Service definitions are TypeScript files in the packages/core/src/services/ directory. Each service has its own file:
packages/core/src/services/
├── databases/
│ ├── qdrant.ts
│ ├── redis.ts
│ ├── postgres.ts
│ └── ...
├── automation/
│ ├── n8n.ts
│ ├── activepieces.ts
│ └── ...
├── search/
│ ├── searxng.ts
│ └── ...
├── ai/
│ ├── ollama.ts
│ ├── whisper.ts
│ └── ...
└── index.ts # Re-exports all servicesThe ServiceDefinition Interface
Every service must conform to the ServiceDefinition interface:
interface ServiceDefinition {
// ── Identity ──────────────────────────────────
id: string; // Unique kebab-case identifier
name: string; // Display name
description: string; // One-sentence description
icon: string; // Emoji icon
category: ServiceCategory; // Category ID
tags: string[]; // Searchable tags
maturity: "stable" | "beta" | "experimental";
// ── Docker ────────────────────────────────────
image: string; // Docker image name
imageTag: string; // Image tag (usually "latest")
ports: Port[]; // Port mappings
volumes: Volume[]; // Volume mounts
environment: EnvVar[]; // Environment variables
healthcheck?: Healthcheck; // Docker healthcheck
command?: string; // Override default CMD
entrypoint?: string; // Override default ENTRYPOINT
// ── Dependencies ──────────────────────────────
requires: string[]; // Required service IDs
conflictsWith: string[]; // Conflicting service IDs
optionalDeps: string[]; // Optional service IDs
// ── Resources ─────────────────────────────────
minMemoryMB: number; // Minimum RAM requirement
gpuCapable: boolean; // Supports GPU acceleration
platforms: string[]; // Supported platforms (linux/amd64, linux/arm64)
// ── Documentation ─────────────────────────────
docsUrl: string; // Official documentation URL
setupNotes?: string; // Post-setup instructions
// ── OpenClaw Integration ──────────────────────
openclawConfig?: {
skillFiles?: string[]; // Skill YAML files to include
envMapping?: Record<string, string>; // Map service vars to OpenClaw vars
};
}Port Type
interface Port {
host: number; // Port on the host machine
container: number; // Port inside the container
protocol?: "tcp" | "udp"; // Default: "tcp"
}Volume Type
interface Volume {
name: string; // Named volume identifier
containerPath: string; // Mount path inside container
description: string; // What this volume stores
}EnvVar Type
interface EnvVar {
name: string; // Variable name
description: string; // What this variable configures
default?: string; // Default value
required: boolean; // Must be set by user
secret: boolean; // Should be auto-generated
}Example: Adding a Custom Service
Let's walk through adding Meilisearch as a new service:
// packages/core/src/services/search/meilisearch.ts
import type { ServiceDefinition } from "../types";
export const meilisearch: ServiceDefinition = {
// Identity
id: "meilisearch",
name: "Meilisearch",
description: "Lightning-fast, typo-tolerant full-text search engine",
icon: "🔎",
category: "search",
tags: ["search", "full-text", "typo-tolerant", "instant"],
maturity: "stable",
// Docker
image: "getmeili/meilisearch",
imageTag: "v1.6",
ports: [
{ host: 7700, container: 7700 },
],
volumes: [
{
name: "meilisearch_data",
containerPath: "/meili_data",
description: "Search index and database storage",
},
],
environment: [
{
name: "MEILI_MASTER_KEY",
description: "Master API key for administration",
required: true,
secret: true,
},
{
name: "MEILI_ENV",
description: "Environment: development or production",
default: "production",
required: false,
secret: false,
},
{
name: "MEILI_NO_ANALYTICS",
description: "Disable telemetry",
default: "true",
required: false,
secret: false,
},
],
healthcheck: {
test: ["CMD", "curl", "-f", "http://localhost:7700/health"],
interval: "10s",
timeout: "5s",
retries: 3,
},
// Dependencies
requires: [],
conflictsWith: [],
optionalDeps: [],
// Resources
minMemoryMB: 256,
gpuCapable: false,
platforms: ["linux/amd64", "linux/arm64"],
// Documentation
docsUrl: "https://www.meilisearch.com/docs",
setupNotes: "Access the dashboard at http://localhost:7700",
// OpenClaw Integration
openclawConfig: {
envMapping: {
MEILI_MASTER_KEY: "OPENCLAW_MEILISEARCH_API_KEY",
},
},
};Registering the Service
After creating the service file, register it in the category's index:
// packages/core/src/services/search/index.ts
export { searxng } from "./searxng";
export { meilisearch } from "./meilisearch"; // Add this lineThen add it to the main service registry:
// packages/core/src/services/index.ts
import { meilisearch } from "./search/meilisearch";
// Add to the allServices array
const allServices: ServiceDefinition[] = [
// ... existing services
meilisearch,
];Testing Your Service
# Build the core package
cd packages/core
pnpm build
# Verify the service appears in the list
pnpm --filter cli start -- --help
# Generate a test stack with your service
pnpm --filter cli start -- test-stack \
--services meilisearch,redis \
--dry-run
# Run the generated docker-compose to verify it works
cd test-stack
docker compose up -d
docker compose ps
curl http://localhost:7700/healthService Guidelines
- Use official Docker images whenever possible
- Pin image tags to specific versions (not
latest) for stability - Mark secrets with
secret: trueso the CLI auto-generates them - Include a healthcheck so Docker can monitor the service
- Declare dependencies accurately — missing deps cause confusing startup errors
- Test on both AMD64 and ARM64 if you claim multi-platform support
- Add the
openclawConfigif the service integrates with OpenClaw skills
Next Steps
- Service Catalog — see all existing services for reference
- Skill Packs — learn how skills connect to services
- Contributing Guide — submit your service as a PR