MonkScript Reference
MonkScript is the YAML-based configuration language used by Monk to define templates.
Basic Syntax and Structure
MonkScript follows standard YAML syntax with some Monk-specific extensions. Each file defines a namespace and one or more entities that represent infrastructure components like applications, databases, or complete stacks.
File Layout
Every MonkScript file must begin with a namespace declaration, followed by entity definitions:
namespace: my-namespace
# Entity definitions followentity-one: defines: runnable # entity configuration...
entity-two: defines: group # entity configuration...
MonkScript files are usually named after the namespace they belong to and can be placed in any directory - no special project layout is required.
Naming Convention
All identifiers in MonkScript must use kebab-case (lowercase letters, numbers, and hyphens as separators):
# Correct (kebab-case)my-entity: defines: runnable
# IncorrectMyEntity: # Don't use PascalCasemy_entity: # Don't use snake_casemyEntity: # Don't use camelCase
This naming convention applies to entity names, variable names, container names, service names, and other identifiers.
Namespace Paths
Namespace paths are used to reference entities within a namespace. They are always relative to the current namespace and are always kebab-case.
Although .
can be used to reference the current namespace, it is recommended to use the namespace name directly.
Paths usually contain two segments separated by a /
: <namespace>/<entity-name>
. Nested paths are supported but discouraged as entities are usually flat.
namespace: my-namespace
my-entity: defines: runnable
Path to the entity above is my-namespace/my-entity
.
Paths can reference entities in other namespaces:
namespace: my-namespace
my-entity: defines: runnable inherits: other-namespace/my-entity
Entity Types
MonkScript supports four main entity types:
Runnable
A runnable is the basic unit of execution in Monk, defining one or more containers that run together in a single pod.
my-app: defines: runnable containers: app: image: nginx:latest # Other sections
Group
A group collects multiple runnables and other entities that will be run together as a logical unit.
my-stack: defines: group members: - my-namespace/my-app - my-namespace/my-db # Limited to variables and metadata
Entity Type Definition
An entity type definition creates a reusable template that can be instantiated. This is a powerful feature for creating your own abstractions.
my-entity: defines: entity schema: # ... # Define reusable components
Entity Instance
Creates an instance of a defined entity type.
my-instance: defines: my-namespace/my-template # Fields as defined in my-namespace/my-template
Deprecated: Fragments
Note: Fragments (configuration blocks without a
defines
property) are deprecated and should be avoided in new code.
Inheritance and Composition
The defines
Property
The defines
property specifies what type an entity is:
defines: runnable
- Creates a runnabledefines: group
- Creates a groupdefines: entity
- Creates an entity type definitiondefines: my-namespace/my-entity
- Creates an instance of an entity type
The inherits
Property
The inherits
property allows entities to inherit configuration from other entities:
my-app: defines: runnable inherits: my-namespace/common-config # More specific configuration
When using inherits
, configuration from the inherited entity is merged with the current entity:
- Simple values in the current entity override values from the inherited entity
- Lists are combined (with duplicates removed)
- Maps/objects are deeply merged, with current values taking precedence for duplicate keys
- This applies recursively to all nested configuration
Multiple inheritance is not supported - an entity can only inherit from one other entity.
Entity Sections by Type
Different entity types support different sections:
Runnable Sections
Runnables support the most comprehensive set of sections:
containers
(required) - Container definitionsvariables
- Entity variablesfiles
- File definitionsconnections
- Connections to other entitiesservices
- Service definitionscheck
- Health/readiness checksdepends
- Dependenciesrecovery
- Recovery configurationaffinity
- Scheduling configurationvolumes
- Persistent volume configurationscale
- Scaling configurationmetadata
- Entity metadata
Group Sections
Groups are limited to:
variables
- Entity variablesmetadata
- Entity metadata
Entity Type Definition Sections
Read the Entity Type Definitions section for more information.
Entity Instance Sections
Read the Entity Type Definitions section for more information.
Variables
Variables provide a way to parameterize your configuration and can be referenced throughout the entity:
variables: port: 8080 database-url: "postgres://user:pass@host:5432/db" debug-mode: type: boolean value: true description: "Enable debug logging" env: DEBUG_MODE # Exposed as an environment variable
Variables can be simple values or complex objects with type information and descriptions.
Containers
Containers define the actual execution units within a runnable (only valid in runnable entities):
containers: web: image: nginx:latest image-tag: 1.21 # Can also use ArrowScript: <- $version ports: - "80:80" # Host:Container format paths: - "/data:/app/data" # Host:Container format environment: - "DEBUG=true" bash: "nginx -g 'daemon off;'" # Command to run
Multiple containers can be defined within a single runnable, creating a pod-like deployment.
Services
Services define the endpoints that a runnable exposes (only valid in runnable entities):
services: http: container: web # Reference to the container port: 80 # Container port protocol: tcp # tcp or udp publish: true # Expose to external network host-port: 8080 # Port on the host description: "HTTP endpoint"
Connections
Connections define how a runnable connects to services provided by other entities (only valid in runnable entities):
connections: db: target: my-namespace/database # Path to the target entity service: postgres # Service name to connect to description: "Main database connection"
Files
Files define configuration files to be created in containers (only valid in runnable entities):
files: nginx-conf: container: web path: /etc/nginx/nginx.conf mode: 0644 # File permissions (octal) contents: | server { listen 80; # ... }
Checks
Checks allow you to define health and readiness checks for your runnables (only valid in runnable entities):
check: readiness: code: 'http-get("http://localhost:8080/health") nil? not' period: 10 # Check interval in seconds initialDelay: 5 # Wait before first check attempts: 5 # Number of attempts before failing
The code
field contains ArrowScript that should return a boolean value.
Dependencies
Dependencies define the order in which entities should be started (only valid in runnable entities):
depends: wait-for: - my-namespace/database - my-namespace/redis
Or with timeout:
depends: wait-for: runnables: - my-namespace/database - my-namespace/redis timeout: 300 # Seconds
Scaling
Configure horizontal scaling for your runnables (only valid in runnable entities):
scale: value: 3 # Static number of instances # Or dynamic scaling value: "cpu() 0.8 gt? then(5) else(3)" # ArrowScript without <- prefix min: 1 # Minimum instances max: 10 # Maximum instances interval: 30 # Check interval in seconds throttle-up: 120 # Seconds between scale ups throttle-down: 300 # Seconds between scale downs
# Metric-based scaling metrics: cpu: 0.7 # Target CPU utilization (70%) memory: 0.8 # Target memory utilization (80%)
Volumes
Define persistent storage volumes (only valid in runnable entities):
volumes: data: kind: persistent # Volume type size: 10 # Size in GB path: /data # Mount path
# Backup configuration backup: every: 1 kind: day # hour, day, week rotation-days: 7 start-time: "02:00" # 24-hour format
Affinity
Control where your runnable gets scheduled (only valid in runnable entities):
affinity: tag: "production" # Run on nodes with this tag name: "worker-1" # Or run on a specific node ignore-pressure: false # Consider node resource pressure resident: true # Reserve the node exclusively
Recovery
Configure how your runnable recovers from failures (only valid in runnable entities):
recovery: after: 10 # Seconds before recovery mode: default # Recovery mode: node, cluster, default when: container-failure # Recovery trigger condition kill-on-recovery: parent # What to kill during recovery
Metadata
Add descriptive information to your entities (valid in all entity types):
metadata: name: "My Web Application" description: "A simple web application with database" website: "https://example.com" source: "https://github.com/example/webapp" publisher: "Example Corp" icon: "https://example.com/icon.png" tags: "web, database, production" private: false # Whether to hide from catalog
ArrowScript
ArrowScript is an embedded scripting language that allows dynamic calculations and transformations within MonkScript. ArrowScript expressions begin with <-
and can be used in most string or numeric fields within Monk YAML files. ArrowScripts are executed on Monk’s control plane at runtime instead of being simple macros.
ArrowScript has a concise concatenative syntax ideal for writing one-liners. You can think of it as Monk’s shell scripting language.
variables: greeting: <- `Hello ${name}!` # Template strings random-port: <- random(3000, 5000) # Operator calls db-ready: <- $db "ready" eq # Value comparison
ArrowScript can be used for:
- String interpolation a’la JavaScript template literals
- Mathematical calculations
- Conditional logic
- Accessing other variables with
$variable-name
- Built-in operators and functions like
random()
,http-get()
, etc.
In some contexts like scale.value
and check.readiness.code
, ArrowScript is written without the <-
prefix.
Entity Type Definitions and Instances
Entity type definitions allow you to create custom resources that extend Monk with your own data structures and logic. Entities can store structured data and execute custom JavaScript code during their lifecycle.
Entity Type Definition
An entity type is defined with defines: entity
and can include a schema to validate its structure:
namespace: example
database-type: defines: entity schema: name: type: string version: type: string port: type: integer variables: default-port: 5432
The schema
property describes the entity structure and follows JSON Schema specification, similar to OpenAPI.
Entity Instances
Once an entity type is defined, you can create instances of it:
postgres-db: defines: example/database-type name: "main-database" version: "14" port: 5432
When you create an instance:
- The instance inherits all properties from the entity type definition
- The instance must provide values that match the schema
- You can override variables and metadata in the instance
Lifecycle Scripts
Entity types can include JavaScript (ES6) lifecycle scripts that execute during different phases:
database-type: defines: entity lifecycle: create: | function main(definition, state, context) { console.log("Creating database:", definition.name); // Create database logic here return { "created": Date.now() }; } start: | function main(definition, state, context) { console.log("Starting database:", definition.name); // Start database logic here return { ...state, "started": Date.now() }; } stop: | function main(definition, state, context) { console.log("Stopping database:", definition.name); // Stop database logic here } purge: | function main(definition, state, context) { console.log("Purging database:", definition.name); // Delete database logic here }
The main
function receives up to 3 arguments:
definition
- Entity data as defined in templatestate
- Saved data from previous executions (can be empty)context
- Additional data with properties like action, status, path
The returned object is saved as state and passed to subsequent operations.
Available Lifecycle Events
create
- Triggered with firstmonk run
start
- Triggered with everymonk run
ormonk update
update
- Triggered withmonk update
stop
- Triggered withmonk stop
purge
- Triggered withmonk purge
sync
- Triggered for any command that has no explicit script
Custom Actions
You can define custom actions that can be called with monk do
:
database-type: defines: entity lifecycle: backup: | function main(definition, state, context) { console.log("Backing up database:", definition.name); // Backup logic here return { ...state, "lastBackup": Date.now() }; }
To use a custom action:
monk do example/postgres-db/backup
Readiness Checks
Entity types can include readiness checks to determine when they’re ready:
database-type: defines: entity checks: readiness: code: | function main(definition, state, context) { // Test if database is ready if (!isReady()) { throw new Error("Database not ready yet"); } return state; } period: 10 # Check interval in seconds initialDelay: 5 # Wait before first check attempts: 12 # Max attempts (fails after this)
Requiring Modules
Entity scripts can use built-in modules for additional functionality:
database-type: defines: entity requires: - http - secret lifecycle: create: | const http = require("http"); const secret = require("secret"); function main(def, state, ctx) { // Use Secret module let password = secret.get(def.secret); let bucketName = def.name; let bucket = http.get(`https://api.example.com/buckets/${bucketName}`, { headers: { "Authorization": `Bearer ${password}` } });
return { bucketName: bucket.name }; }
Available modules include:
cli
- CLI operationssecret
- Secret managementfs
- File system operationsparser
- Parsing utilitieshttp
- HTTP clientgcp
,aws
,azure
,do
- Cloud provider APIs
Entity References
Entities can reference other entities using ArrowScript:
my-app: defines: runnable variables: db-host: <- entity-state("example/postgres-db") get-member("address") db-port: <- entity("example/postgres-db") get-member("port") containers: app: environment: - <- `DB_HOST=${db-host}` - <- `DB_PORT=${db-port}`
This allows components to properly reference and use resources managed by custom entities.
Complete Entity Type Example
namespace: example
cloud-bucket: defines: entity schema: required: ["name", "region"] name: type: string region: type: string public: type: boolean default: false requires: - cloud/aws lifecycle: create: | function main(def, state, ctx) { console.log("Creating bucket:", def.name);
let result = aws.createS3Bucket(def.name, { region: def.region, public: def.public });
if (result.error) { throw new Error("Failed to create bucket: " + result.error); }
return { "arn": result.arn, "url": `https://${def.name}.s3.amazonaws.com` }; } purge: | function main(def, state, ctx) { console.log("Deleting bucket:", def.name);
let result = aws.deleteS3Bucket(def.name);
if (result.error) { throw new Error("Failed to delete bucket: " + result.error); } } check-access: | function main(def, state, ctx) { let result = aws.checkS3BucketAccess(def.name); return { "accessible": result.accessible }; } checks: readiness: code: | function main(def, state, ctx) { let result = aws.getS3Bucket(def.name); if (result.error) { throw new Error("Bucket not accessible yet"); } return state; } period: 5 attempts: 10
Complete Example
Here’s a complete example that demonstrates many MonkScript features:
namespace: example
# A web applicationweb-app: defines: runnable
metadata: name: "Example Web App" description: "A web application with database" tags: "web, example"
variables: http-port: 8080 version: "1.0.0" debug: true log-level: <- $debug ? "debug" : "info"
containers: web: image: nginx image-tag: <- $version ports: - "80:80" environment: - <- `LOG_LEVEL=${log-level}`
services: http: container: web port: 80 protocol: tcp publish: true host-port: <- $http-port
files: nginx-conf: container: web path: /etc/nginx/conf.d/default.conf contents: | server { listen 80; location / { root /usr/share/nginx/html; } }
check: readiness: code: "http-get(\"http://localhost:80\") nil? not" period: 5 attempts: 3
scale: value: 2 min: 1 max: 5
This example defines a web application that uses variables and ArrowScript, and defines containers, services, files, checks, and scaling options.