routeros-app-yaml — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited routeros-app-yaml (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
RouterOS 7.21 introduced the /app path (built-in app listing and management). The full YAML app creation feature (/app/add) appeared in 7.22 (first seen in 7.22beta5). Think of it as MikroTik's opinionated alternative to docker-compose — but it is NOT docker-compose, with significant differences.
The /app subsystem lets users define one or more containers as a single "application" in YAML. RouterOS parses the YAML, creates containers, volumes, networks, and config files, then manages the lifecycle.
Key concepts:
/app is defined by a YAML document with services, configs, volumes, and networks/app/add yaml-url=...) or REST APIGET /rest/app)app-store-urls=) provide curated collections| Feature | docker-compose | RouterOS /app |
|---|---|---|
| Port format | host:container[/protocol] | Two styles (see below) |
| Environment | KEY=value or list | Same, but placeholders expand |
| Volumes | Named or bind mounts | Subset — no bind mount options |
| Networks | Full docker network model | Simplified — name + external |
| Build | Full Dockerfile support | Minimal (context + dockerfile) |
| Configs | Docker configs API | Inline content only |
| Deploy/resources | Yes | No — not supported |
Top-level version: | Deprecated (was required) | Not used |
| File extension | .yml / .yaml | .tikapp.yaml (convention) |
| Property | Type | Required | Description |
|---|---|---|---|
name | string | No | Unique identifier for the /app |
descr | string | No | Human-readable description shown in app UI |
page | string (URI) | No | Project homepage URL |
category | string (enum) | No | App store classification |
icon | string (URI) | No | Icon URL (shown in WebFig with show-in-webfig=yes) |
default-credentials | string or null | No | username:password shown in UI |
url-path | string | No | URL path suffix for browser access (e.g., /admin) |
credentials | string | No | Credential hint (alternative to default-credentials) |
option | boolean | No | Whether app is optional |
auto-update | boolean | No | Pull and restart containers on every boot |
services | object | Yes | Container service definitions (≥1 required) |
volumes | object | No | Named volume declarations |
networks | object | No | Network declarations |
configs | object | No | Config file declarations with inline content |
productivity, storage, networking, development, communication,
file-management, search, video, media, media-management,
home-automation, monitoring, database, automation, ai,
messaging, radio, security, businessNew categories appear when MikroTik adds built-in apps. The CI schema validation catches these.
Each key under services: defines one container. Required property: image.
| Property | Type | Description |
|---|---|---|
image | string | Container image (omit registry to use /container/config's registry-url) |
container_name | string | Explicit name; used as base for file paths under /container |
hostname | string | Container hostname |
entrypoint | string or string[] | Override default entrypoint |
command | string or string[] | Override default command |
ports | array | Port mappings (see format section) |
environment | object or array or null | Environment variables (KEY=value list or {KEY: value} map) |
volumes | array of strings | Volume mounts (e.g., my-vol:/data) |
configs | array of objects | Config file placements ({source, target, mode}) |
restart | enum | no, always, on-failure, unless-stopped |
depends_on | array or object | Service dependency ordering |
devices | array of strings | Device mappings passed to container |
user | string | User to run container as |
security_opt | array of strings | Security options |
shm_size | string | Shared memory size |
stop_grace_period | string or int | Time before SIGKILL |
ulimits | object | Resource limits (e.g., nofile: {soft: 65536, hard: 65536}) |
build | object or string | Build configuration (context, dockerfile, args) |
healthcheck | object | Health check (test, interval, timeout, retries, start_period) |
stdin_open | boolean | Keep stdin open |
expose | array | Internal ports (not published to host) |
secrets | array of strings | Secrets to expose |
attach | boolean | Attach to stdio |
RouterOS supports two port mapping string formats. Both are valid; new apps from 7.23beta2+ prefer the colon style.
[ip:]host_port:container_port[/tcp|/udp][:label]Examples:
ports:
- "8080:80/tcp"
- "8443:443/tcp:https"
- "192.168.1.1:53:53/udp:dns"[ip:]host_port:container_port[:label][:tcp|:udp]Protocol is appended with colon instead of slash:
ports:
- "8080:80:web:tcp"
- "8443:443:https:tcp"
- "53:53:dns:udp"ports:
- target: 80
published: 8080
protocol: tcp
name: web
app_protocol: httpPort strings can include literal IPs or placeholder expressions:
ports:
- "[accessIP]:[accessPort]:80/tcp:web" # Old style with placeholders
- "[accessIP]:[accessPort]:80:web:tcp" # New style with placeholdersRouterOS expands these placeholders at deploy time:
| Placeholder | Expands to |
|---|---|
[accessIP] | IP address for accessing the app from outside |
[accessPort] | Primary host port for external access |
[accessPort2] | Secondary host port |
[containerIP] | IP address assigned to the container |
[routerIP] | Router's own IP address |
Placeholders appear in port mappings, environment values, and config content.
Top-level configs: declares config content; services reference them:
configs:
my-config:
content: |
server {
listen 80;
server_name [accessIP];
}
services:
web:
image: nginx:alpine
configs:
- source: my-config
target: /etc/nginx/conf.d/default.conf
mode: 0644volumes:
app-data: {} # Named volume (null or empty object)
networks:
app-net:
name: my-network
external: true # Use existing RouterOS networkRouterOS can load app collections from URLs configured via app-store-urls=. The store format is simply a YAML array of /app definitions:
- name: app-one
services:
web:
image: nginx:alpine
- name: app-two
services:
db:
image: postgres:16Store files use the .tikappstore.yaml extension by convention.
// List all /app entries (built-in + custom)
const apps = await fetch(`${base}/app`, auth).then(r => r.json());
// Each entry has: .id, name, yaml (raw YAML string), and metadata
// The 'yaml' field is a RouterOS string containing the full YAML document
// Add a custom /app from URL
await fetch(`${base}/app`, {
method: "PUT",
...auth,
body: JSON.stringify({ "yaml-url": "https://example.com/my-app.tikapp.yaml" }),
});Note: The /app endpoint requires the container extra package to be installed.
Two schema variants exist for each /app document:
| Schema | Purpose | Port validation | Env var names |
|---|---|---|---|
*.latest.json | CI/strict validation | Regex patterns enforced | ^[A-Z_][A-Z0-9_]*$ only |
*.editor.json | Editor/SchemaStore UX | No regex (allows autocompletion) | Case-insensitive |
The strict schema has regex pattern on port strings which prevents VSCode autocompletion — the YAML extension won't suggest values for fields with patterns. The editor variant removes these patterns.
Add to VSCode settings for YAML autocompletion:
{
"yaml.schemas": {
"https://tikoci.github.io/restraml/routeros-app-yaml-schema.latest.json": "*.tikapp.yaml",
"https://tikoci.github.io/restraml/routeros-app-yaml-store-schema.latest.json": "*.tikappstore.yaml"
}
}Use .editor.json URLs for better autocompletion at the cost of less strict validation.
:tcp/:udp suffix)devices, expose, secrets, attach)*.editor.json for mixed case/app returns 404 without the container extra packagerouteros-fundamentals skillrouteros-qemu-chr skill~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.