Skip to main content
You can build Moru templates directly from Dockerfiles, making it easy to migrate existing Docker-based workflows.

From Dockerfile Content

from moru import Template

dockerfile = """
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["python", "app.py"]
"""

template = Template().from_dockerfile(dockerfile)
info = Template.build(template, alias="my-python-app")

From Dockerfile Path

from moru import Template

# Read from file path
template = Template().from_dockerfile("./Dockerfile")
info = Template.build(template, alias="my-app")

Supported Instructions

InstructionSupportReason
FROM✅ FullSets the base image for the template
RUN✅ FullCommands executed during template build
COPY✅ FullFiles uploaded and copied into the template
WORKDIR✅ FullSets the working directory for subsequent commands
ENV✅ FullEnvironment variables persisted in the template (both KEY=value and KEY value formats)
USER✅ FullSets the user context for subsequent commands
CMD✅ FullConverted to Moru’s start command
ENTRYPOINT⚠️ PartialConverted to CMD - ENTRYPOINT and CMD are not distinguished
ADD⚠️ PartialLocal files only - URL sources not supported (use RUN curl or RUN wget instead)
ARG❌ IgnoredBuild arguments not expanded - use ENV instead for runtime variables
EXPOSE❌ IgnoredNot needed - all ports are accessible in Moru sandboxes by default
VOLUME❌ IgnoredNot applicable - Moru sandboxes use ephemeral filesystems
HEALTHCHECK❌ IgnoredUse Moru’s ready_cmd instead for health checks
LABEL❌ IgnoredMetadata labels not used in Moru templates
STOPSIGNAL❌ IgnoredMoru manages sandbox lifecycle directly
SHELL❌ IgnoredMoru uses the default shell

Why Some Instructions Are Ignored

Moru templates are not Docker containers. They run in lightweight Firecracker microVMs with a different execution model:
  • No port mapping needed: Sandboxes expose all ports directly - EXPOSE is unnecessary
  • Ephemeral by design: Sandbox filesystems reset on restart - VOLUME doesn’t apply
  • Build-time vs runtime: ARG is for Docker build-time only; use ENV for Moru templates
  • Native health checks: Use set_ready_cmd() instead of HEALTHCHECK for better integration

Example: Node.js Application

Dockerfile:
FROM node:20-slim

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy application
COPY . .

# Start
EXPOSE 3000
CMD ["node", "server.js"]
Build it:
template = Template().from_dockerfile("./Dockerfile")

# Add a ready command for health checks
template = template.set_ready_cmd("curl -s localhost:3000/health")

info = Template.build(template, alias="my-node-app")

Not Supported: Multi-stage Builds

Multi-stage Dockerfiles are not supported. If your Dockerfile contains multiple FROM instructions, you’ll get an error.
If you have a multi-stage Dockerfile like this:
# This will NOT work with Moru
FROM node:20 AS builder
WORKDIR /app
RUN npm ci && npm run build

FROM node:20-slim
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/server.js"]
You have two options:

Option 1: Build locally, use production image

Build your application locally, then create a template from the production stage only:
# production.Dockerfile - single stage
FROM node:20-slim
WORKDIR /app
COPY dist ./dist
COPY node_modules ./node_modules
CMD ["node", "dist/server.js"]
# Build locally first: npm run build
template = Template().from_dockerfile("./production.Dockerfile")
info = Template.build(template, alias="my-built-app")

Option 2: Use the Template Builder API

For more complex builds, use the builder API directly:
template = (
    Template()
    .from_image("node:20-slim")
    .set_workdir("/app")
    .copy("./dist", "./dist")
    .copy("./node_modules", "./node_modules")
    .set_start_cmd("node dist/server.js")
)
info = Template.build(template, alias="my-built-app")

CLI Build

Use the CLI to build directly from Dockerfile:
# Basic build
moru template create my-app --dockerfile ./Dockerfile

# With start and ready commands
moru template create my-app \
  --dockerfile ./Dockerfile \
  --cmd "node server.js" \
  --ready-cmd "curl localhost:3000/health"

# With more resources
moru template create my-app \
  --dockerfile ./Dockerfile \
  --cpu-count 4 \
  --memory-mb 2048

Combining with Builder API

You can extend a Dockerfile-based template with additional configuration:
template = (
    Template()
    .from_dockerfile("./Dockerfile")
    .set_envs({"NODE_ENV": "production"})
    .apt_install(["curl"])  # For health checks
    .set_start_cmd("node server.js", "curl -s localhost:3000/health")
)

info = Template.build(template, alias="my-extended-app")

Limitations Summary

LimitationWorkaround
Multi-stage buildsBuild locally, use single-stage Dockerfile for final image
ARG build argumentsUse ENV for runtime variables
ADD with URLsUse RUN curl -O <url> or RUN wget <url>
COPY --from=<stage>Not supported without multi-stage builds
HEALTHCHECKUse set_ready_cmd() / --ready-cmd instead
See the Supported Instructions table for the complete list of what’s supported and why.

Best Practices

  1. Use slim base images: Smaller images build faster
  2. Layer caching: Put rarely-changing instructions first
  3. Combine RUN commands: Reduce layers for faster builds
  4. Add health checks: Use set_ready_cmd() for reliable sandbox startup

Next Steps