Deployment

Self-Hosting

Running Manic on your own infrastructure using Bun.

Self-Hosting

Manic can be deployed anywhere that supports the Bun runtime. This is the most flexible deployment method, allowing you to run on Linux servers, Docker, or edge platforms like Fly.io.

Setup

manic build writes a .manic/ directory (by default) with server.js, the client bundle, and API bundles. Run manic start to launch the Bun server.

You do not need a deployment provider for plain self-hosting. Providers from @manicjs/providers (vercel, cloudflare, netlify) only adapt that output for specific platforms.

The listen port defaults to 6070 from config (server.port). manic start also sets the PORT environment variable for the process (CLI --port / -p overrides).

Running the Server

  1. Build the application:

    manic build
  2. Start the production server:

    manic start

By default, the server listens on port 6070. Override with server.port in manic.config.ts, PORT in the environment, or manic start --port.

Docker Deployment

Create a Dockerfile:

FROM oven/bun:latest AS builder

WORKDIR /build
COPY . .
RUN bun install
RUN bun run build

FROM oven/bun:latest

WORKDIR /app
COPY --from=builder /build/.manic .manic
COPY --from=builder /build/node_modules node_modules

ENV NODE_ENV=production
ENV PORT=6070
EXPOSE 6070

CMD ["bun", ".manic/server.js"]

Build and run:

docker build -t my-manic-app .
docker run -p 6070:6070 -e NODE_ENV=production my-manic-app

Systemd Service

Deploy with systemd on Linux:

# /etc/systemd/system/manic.service
[Unit]
Description=Manic Application
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/manic
Environment="NODE_ENV=production"
Environment="PORT=6070"
ExecStart=/usr/bin/bun /var/www/manic/.manic/server.js
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl enable manic
sudo systemctl start manic
sudo systemctl status manic

View logs:

sudo journalctl -u manic -f

Nginx Reverse Proxy

Configure Nginx to proxy requests:

upstream manic {
  server localhost:6070;
}

server {
  listen 80;
  server_name example.com;
  
  # Enable Gzip compression
  gzip on;
  gzip_types text/plain text/css application/json application/javascript;
  
  location / {
    proxy_pass http://manic;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_cache_bypass $http_upgrade;
  }
}

Enable HTTPS with Certbot:

sudo certbot --nginx -d example.com

Production Checklist

  • Reverse Proxy: Use Nginx or Caddy for SSL/TLS and compression
  • Process Manager: Use systemd, Docker, or PM2 for auto-restart
  • Environment: Set NODE_ENV=production
  • Port: Firewall configured to allow incoming traffic
  • Monitoring: Set up logging and error tracking (Sentry, Grafana)
  • Backups: Database and configuration backups automated
  • Health Checks: Implement /health endpoint for uptime monitoring
  • Rate Limiting: Add rate limiting via Nginx or middleware

Health Check Endpoint

Add a simple health check:

// app/api/health/index.ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) =>
  c.json({ status: 'ok', timestamp: new Date().toISOString() })
);

export default app;

Monitor with a simple script:

#!/bin/bash
while true; do
  curl -f http://localhost:6070/api/health || systemctl restart manic
  sleep 30
done

Performance Tuning

Bun Server Options

Pass options to the manic start command (for example manic start --port 8080). The production entry point is .manic/server.js after manic build.

# Raise open-file limit on Linux before high-traffic deploys
ulimit -n 65536

Database Connection Pooling

Use connection pools for databases:

// app/lib/db.ts
import { Pool } from 'pg';

export const db = new Pool({
  max: 20,      // Max connections
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

Static Asset Caching

Configure HTTP caching headers in API routes:

// app/api/example/index.ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => {
  c.header('Cache-Control', 'public, max-age=3600');
  return c.json({ data: 'cached' });
});

export default app;

On this page