Stop Containerizing Your 50-Line Scripts

Published: (December 25, 2025 at 05:00 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for Stop Containerizing Your 50-Line Scripts

Levskiy

Introduction

We’ve all been there. You need to deploy a simple webhook handler — maybe 50 lines of code. What do you do?

  • Write a Dockerfile
  • Set up docker‑compose.yml
  • Pull a 500 MB Node.js base image
  • Install dependencies
  • Configure environment variables
  • Set up a reverse proxy

For 50 lines of code.

Your VPS bloats with container layers. Each micro‑task runs in its own Node.js process eating RAM. Updating a single line feels like a deployment ritual.

What if there was a simpler way?

M3M (Mini Services Manager) is a single Go binary that acts as a private runtime for your JavaScript services. No containers. No npm dependency hell. No cloud bills for things that should run on your $5 VPS.

Show Me the Code

$service.boot(() => {
  const users = $database.collection('users');

  // HTTP Endpoint
  $router.get('/users', (ctx) => {
    return { users: users.find({}) };
  });

  $router.post('/users', (ctx) => {
    const user = users.insert(ctx.body);
    return { user };
  });

  // Cron Task (Every hour)
  $schedule.every('1h', () => {
    $logger.info('Total users:', users.count({}));
  });
});

$service.start(() => {
  $logger.info('Service is ready!');
});

That’s it. No imports. No package.json. No build step. Write it in the browser, hit Save, and it runs.

Built‑in Batteries

Every service has access to powerful modules via the $ prefix:

CategoryModules
Core$service, $router, $schedule, $logger, $env
Data$database, $storage, $goals
Network$http, $mail
Utils$crypto, $encoding, $utils, $validator
Media$image, $draw

Routing with Middleware

$service.boot(() => {
  // Auth middleware
  const auth = (ctx) => {
    const token = ctx.header('Authorization');
    if (!token) {
      ctx.status(401);
      return { error: 'Unauthorized' };
    }
  };

  // Protected routes
  const api = $router.group('/api', auth);
  api.get('/profile', (ctx) => {
    return { user: ctx.get('user') };
  });
});

Database with MongoDB‑style Queries

const posts = $database.collection('posts');

// Insert
const post = posts.insert({
  title: 'Hello World',
  views: 0,
  tags: ['intro', 'first']
});

// Query with operators
const popular = posts.find({
  views: { $gte: 100 },
  tags: { $contains: 'featured' }
});

// Update
posts.update(
  { _id: post._id },
  { $inc: { views: 1 } }
);

Scheduled Tasks

// Every 5 minutes
$schedule.every('5m', () => {
  $logger.info('Checking for updates...');
});

// Cron expression
$schedule.cron('0 9 * * *', () => {
  // Every day at 9 AM
  sendDailyReport();
});

// One‑time delayed execution
$schedule.after('30s', () => {
  $logger.info('30 seconds have passed!');
});

HTTP Client

const response = $http.post(
  'https://api.example.com/webhook',
  {
    event: 'user_signup',
    data: { email: 'user@example.com' }
  },
  {
    headers: { 'X-API-Key': $env.get('API_KEY') }
  }
);

if (response.ok) {
  $logger.info('Webhook sent successfully');
}

Service Lifecycle

M3M services have three lifecycle phases:

// 1. Boot: Initialize routes, schedules, connections
$service.boot(() => {
  $router.get('/health', () => ({ status: 'ok' }));
});

// 2. Start: Service is ready, load initial data
$service.start(() => {
  $logger.info('Service started!');
});

// 3. Shutdown: Clean up before stopping
$service.shutdown(() => {
  $logger.info('Goodbye!');
});

Performance That Makes Sense

M3M runs comfortably on a $5/month VPS with 512 MB RAM — even with 20+ active services. The entire binary (including the web UI) is ~30 MB.

Compare that to a typical setup where each Node.js container takes 100–200 MB minimum.

Getting Started

Install

# Clone
git clone https://github.com/levskiy0/m3m.git
cd m3m

# Build (requires Go 1.24+, Node.js 20+)
make build

# Create first admin user
./build/m3m new-admin admin@example.com yourpassword

# Run
./build/m3m serve

Real‑World Use Cases

M3M shines for:

  • Webhook handlers – Receive and process webhooks from Stripe, GitHub, etc.
  • Telegram/Discord bots – Self‑hosted bots without the container tax
  • Internal APIs – Quick REST APIs for your tools
  • Scheduled tasks – Cron jobs with a nice UI
  • Data processors – ETL pipelines, scrapers, aggregators
  • Monitoring – Health checks, alerting, metrics collection

Claude Code Integration

M3M includes an MCP server for Claude Code. Once connected, Claude knows the entire M3M API and can help write your services:

make build-mcp
claude mcp add m3m-api ./build/m3m-mcp

Now you can ask Claude: “Write me an M3M service that monitors …” and get a ready‑to‑run implementation.

**M3M***“Make My Monitor”* – is a tiny, single‑file Node.js utility that:

- **Monitors a website** and sends alerts via email when it’s down.
- Generates working code using the correct `$http`, `$schedule`, and `$mail` modules.

The Philosophy

M3M is for developers who want to:

  • Write logic in the browser, hit Save, and walk away.
  • Avoid managing Docker for every 50‑line script.
  • Keep server resources for actual work.
  • Dodge npm dependency hell for micro‑tasks.

It’s not trying to replace Kubernetes or compete with serverless platforms.
M3M is a practical tool for the practical developer who has better things to do than write Dockerfiles.

Back to Blog

Related posts

Read more »