shiplet
v0.2.0
Published
Docker/Podman-powered dev environment CLI for Node.js — with release pipeline, snapshots, health dashboard, and more.
Downloads
245
Maintainers
Readme
🌊 Shiplet
A lightweight, Docker-powered development environment for Node.js projects — inspired by Laravel Sail, built for the JS ecosystem.
No Docker knowledge required. One command gets you a fully containerised Node app with databases, mail, object storage, and more.
Table of Contents
- 🌊 Shiplet
- Table of Contents
- Introduction
- Quick Start
- Installation
- Initialising a Project
- Starting and Stopping
- Executing Commands
- Running Tests
- Working with Databases
- Additional Services
- Adding Services Post-Init
- Environment Variables
- Container Logs
- Container Status
- Sharing Your App
- Rebuilding Images
- Customisation (Ejecting)
- Node Version
- Package Manager
- Project Templates
- Generated File Reference
- Requirements
- Container Runtime (Docker & Podman)
- Release Pipeline
- Container Health Dashboard
- Volume Snapshots
- Linting
- Scaling Services
- shiplet.config.json
- Web Dashboard
- Examples
- License
Introduction
Shiplet is a CLI tool that wraps Docker Compose into a set of simple, memorable commands purpose-built for Node.js development. It is the spiritual equivalent of Laravel Sail for PHP, but designed with the Node.js ecosystem in mind:
- Supports npm, yarn, and pnpm out of the box
- Auto-detects your test runner (jest, vitest, mocha)
- Auto-detects which database CLI to open (
shiplet db) - Works with Express, Fastify, NestJS, Next.js, Nuxt, T3 via built-in templates
- Includes an
envcommand for full.envmanagement - Can tunnel your local app to the internet with
shiplet share - Runs via
npx— no global install needed
At its core, Shiplet is a shiplet.yml (Docker Compose) file and a thin CLI wrapper. You can eject the Dockerfiles at any time with shiplet publish for full control.
Shiplet is supported on macOS, Linux, and Windows (via WSL2).
Quick Start
# In a new or existing Node.js project:
npx shiplet init
# Start everything
shiplet up -d
# Install your npm deps inside the container
shiplet npm install
# Open a shell
shiplet shell
# Run your tests
shiplet testInstallation
Run via npx (zero-install)
The fastest way to initialise Shiplet in any project — no global install needed:
npx shiplet initAfter init, all subsequent shiplet commands are available through ./node_modules/.bin/shiplet (if added as a dev dep) or globally.
Install globally
npm install -g shiplet
# or
yarn global add shiplet
# or
pnpm add -g shipletAdd to an existing project
npm install --save-dev shiplet
npx shiplet initShell alias
To avoid typing ./node_modules/.bin/shiplet every time, add an alias to your shell config (~/.zshrc or ~/.bashrc):
alias shiplet='npx shiplet'Or if installed locally in every project:
alias shiplet='./node_modules/.bin/shiplet'Restart your shell, then you can simply type shiplet up, shiplet shell, etc.
Initialising a Project
Run the interactive setup wizard:
shiplet initYou will be prompted to choose:
| Option | Description |
| ------------------- | ---------------------------------------------------------------------------------------- |
| App name | Used as the Docker Compose project name |
| Template | express, fastify, nestjs, nextjs, nuxt, t3, or blank |
| Node version | 22, 20, or 18 (inside the container) |
| Package manager | npm, yarn, or pnpm (auto-detected from lock files) |
| Port | The host port your app will be accessible on |
| Services | Any combination of postgres, mysql, mongo, redis, mailpit, minio, elasticsearch, adminer |
| Timezone | Container timezone (default: UTC) |
To skip all prompts and use defaults:
shiplet init --yes
shiplet init --template nestjs --yesAfter init, Shiplet creates:
your-project/
├── shiplet.yml ← Docker Compose file (edit freely)
├── .env ← Environment variables (added to existing .env)
└── .shiplet/
└── Dockerfile ← App container DockerfileStarting and Stopping
Start all containers defined in shiplet.yml:
shiplet upStart in detached (background) mode:
shiplet up -dStart and force a rebuild of images first:
shiplet up --buildOnce running, your app is accessible at http://localhost:3000 (or whichever port you chose).
Stop all containers (containers are removed, data volumes are preserved):
shiplet downStop and destroy all volumes (this deletes database data — use with caution):
shiplet down -vExecuting Commands
When using Shiplet, your application runs inside a Docker container. Shiplet provides shortcuts to run common commands without leaving your terminal.
Node.js commands
# Check the Node version inside the container
shiplet node --version
# Run a script
shiplet node scripts/seed.jsPackage manager commands
Shiplet proxies all package manager commands into the app container:
# npm
shiplet npm install
shiplet npm run dev
shiplet npm run build
# yarn
shiplet yarn
shiplet yarn add express
# pnpm
shiplet pnpm install
shiplet pnpm add fastify
# npx (inside the container)
shiplet npx prisma migrate dev
shiplet npx ts-node src/server.tsOne-off exec
Run any command inside any running container:
shiplet exec app node -e "console.log('hello')"
shiplet exec redis redis-cli info
shiplet exec postgres psql -U shiplet -d appInteractive shell
Open a bash shell inside the app container (falls back to sh if bash is unavailable):
shiplet shellOpen a shell in a different service:
shiplet shell postgres
shiplet shell redisRunning Tests
Shiplet automatically detects your test runner by inspecting devDependencies in package.json:
| Detected dependency | Command used |
| --------------------- | ---------------- |
| vitest | npx vitest run |
| jest | npx jest |
| mocha | npx mocha |
| (none of the above) | npm test |
# Run all tests
shiplet test
# Pass flags through to your test runner
shiplet test --coverage
shiplet test --watch
shiplet test src/user.test.tsWorking with Databases
PostgreSQL
When Postgres is enabled, it runs in the postgres service. Your app connects to it at the host postgres (the Docker service name) on port 5432.
To connect from your machine (e.g. TablePlus or psql):
- Host:
localhost - Port:
5432(orPOSTGRES_PORTfrom.env) - User / Password / DB: as set in
.env
Open the Postgres CLI inside the container:
shiplet db postgres
# or simply (auto-detected):
shiplet dbMySQL
MySQL runs in the mysql service. Connect your app using host mysql, port 3306.
shiplet db mysql
# or:
shiplet dbMongoDB
MongoDB runs in the mongo service. Your MONGODB_URI in .env is pre-configured to point at mongo:27017.
shiplet db mongo
# or:
shiplet dbRedis
Redis runs in the redis service. Connect your app using redis://redis:6379.
shiplet db redis
# or:
shiplet dbTip:
shiplet dbwith no argument auto-detects the first running database service.
Additional Services
Mailpit (email)
Mailpit intercepts all outgoing SMTP mail from your app and displays it in a web UI — no real emails are sent during development.
- SMTP host/port:
mailpit:1025 - Web UI: http://localhost:8025
Configure your mailer (e.g. Nodemailer):
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST, // mailpit
port: process.env.SMTP_PORT, // 1025
});MinIO (S3)
MinIO provides an S3-compatible object storage API for local development.
- API endpoint:
http://minio:9000(from inside containers),http://localhost:9000(from host) - Console UI: http://localhost:9001
Configure the AWS SDK:
const s3 = new S3Client({
endpoint: process.env.S3_ENDPOINT, // http://minio:9000
region: 'us-east-1',
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
},
forcePathStyle: true,
});Elasticsearch
Elasticsearch runs with security disabled for local development. Access it at http://elasticsearch:9200 from your app or http://localhost:9200 from your host.
Adminer (DB GUI)
Adminer is a lightweight browser-based database manager.
- URL: http://localhost:8080
- Works with PostgreSQL, MySQL, MongoDB, and more.
Adding Services Post-Init
You can add more services to an existing Shiplet project at any time:
# Interactive picker
shiplet add
# Add specific services directly
shiplet add redis mailpit
shiplet add elasticsearch adminerAvailable services: postgres, mysql, mongo, redis, mailpit, minio, elasticsearch, adminer
After adding services, rebuild and restart:
shiplet up --buildEnvironment Variables
Shiplet includes a full .env management command.
# List all variables
shiplet env list
# Get a single variable
shiplet env get DATABASE_URL
# Set a variable
shiplet env set NODE_ENV production
shiplet env set DATABASE_URL=postgresql://shiplet:secret@postgres:5432/app
# Remove a variable
shiplet env unset OLD_KEY
# Sync missing keys from .env.example → .env
shiplet env syncThe sync action is particularly useful after pulling changes from git where .env.example has new keys.
Container Logs
# Tail the last 100 lines from all services
shiplet logs
# Follow (stream) logs in real time
shiplet logs -f
# Tail a specific service
shiplet logs app
shiplet logs postgres
# Show the last 50 lines
shiplet logs -n 50 appContainer Status
shiplet status
# or the alias:
shiplet psDisplays a colourised table of all services, their status, and port mappings:
NAME STATUS PORTS
myapp-app-1 running (Up) 0.0.0.0:3000->3000/tcp
myapp-postgres-1 running (Up) 0.0.0.0:5432->5432/tcp
myapp-redis-1 running (Up) 0.0.0.0:6379->6379/tcpSharing Your App
Expose your local app to the internet using a secure tunnel:
shiplet shareThis uses localtunnel and outputs a public URL that anyone can visit.
# Specify a port (default: 3000)
shiplet share --port 4000
# Request a specific subdomain
shiplet share --subdomain my-demoPress Ctrl+C to stop sharing.
Rebuilding Images
After changing Node version, package manager, or editing .shiplet/Dockerfile:
shiplet buildForce a full rebuild without cache (useful after system package changes):
shiplet build --no-cacheOr rebuild on the next up:
shiplet up --buildCustomisation (Ejecting)
Shiplet ships with a ready-made Dockerfile stored in .shiplet/Dockerfile. To gain full control, eject it to your project root:
shiplet publishThis copies the Dockerfile to docker/Dockerfile. You can then edit it freely — add system packages, change the base image, install global tools, etc.
Update shiplet.yml to point at the ejected file:
services:
app:
build:
context: .
dockerfile: docker/DockerfileSince Shiplet is just Docker Compose under the hood, any valid Compose configuration works in shiplet.yml.
Node Version
The Node.js version is set at build time via a Docker build argument. To change it, update shiplet.yml:
services:
app:
build:
args:
NODE_VERSION: "22" # or "20", "18"Then rebuild:
shiplet build --no-cache
shiplet upPackage Manager
The package manager is also baked into the image via corepack. To change it, update shiplet.yml:
services:
app:
build:
args:
PACKAGE_MANAGER: "pnpm" # npm | yarn | pnpmRebuild the image after changing this.
Project Templates
When running shiplet init, you can select a project template:
| Template | Description |
| --------- | ------------------------------------------------ |
| blank | Bare Node.js container, no framework scaffolding |
| express | Express.js with a minimal app structure |
| fastify | Fastify with plugins pre-configured |
| nestjs | NestJS with TypeScript |
| nextjs | Next.js (App Router) |
| nuxt | Nuxt 3 |
| t3 | T3 stack (Next.js + tRPC + Prisma + Tailwind) |
Or pass via CLI flag:
npx shiplet init --template nestjsGenerated File Reference
shiplet.yml
The Docker Compose file for your project. Edit it directly to customise ports, add environment variables, mount extra volumes, or add any service available on Docker Hub.
.shiplet/Dockerfile
The Dockerfile for your app container. Contains the base Node image, timezone setup, and package manager initialisation. Eject with shiplet publish for full control.
.env
Shiplet appends its required variables to your .env file on init. Variables are namespaced to avoid collisions with your existing config.
Requirements
- Docker Desktop (macOS / Windows) or Docker Engine + Compose plugin (Linux) — install here
- Node.js ≥ 16 on the host (only needed to run the
shipletCLI itself — your app runs inside the container)
Container Runtime (Docker & Podman)
Shiplet supports both Docker and Podman as container runtimes. The runtime is auto-detected at startup — no configuration needed unless you want to pin one explicitly.
Auto-detection priority
SHIPLET_RUNTIME=dockerorSHIPLET_RUNTIME=podmanenvironment variableruntimefield inshiplet.config.json(set byshiplet initorshiplet runtime switch)- Auto-detect: Podman wins if available and running, otherwise Docker
Runtime commands
# Show which runtime is active and why
shiplet runtime show
# Interactively switch between docker and podman
shiplet runtime switch
# Validate both runtimes — checks binary, daemon, and compose plugin
shiplet runtime checkForcing a runtime for a single command
SHIPLET_RUNTIME=podman shiplet up -d
SHIPLET_RUNTIME=docker shiplet build --no-cachePodman-specific notes
Shiplet uses podman compose (bundled in Podman ≥ 4.7) or falls back to the standalone podman-compose package. Install it with:
pip3 install podman-compose
# or update Podman to ≥ 4.7Rootless Podman is fully supported. If you see permission errors on volume mounts, ensure your user has the correct subuid/subgid mappings:
podman system migrateRelease Pipeline
shiplet release is a complete, opinionated release pipeline that handles everything from pre-flight checks to git tagging and npm publishing.
Basic usage
# Bump patch version (1.0.0 → 1.0.1)
shiplet release
# Bump minor version (1.0.0 → 1.1.0)
shiplet release minor
# Bump major version (1.0.0 → 2.0.0)
shiplet release major
# Explicit version
shiplet release 2.4.0
# Pre-release tag (1.0.0 → 1.0.1-beta.0)
shiplet release patch --pre beta
# Release candidate
shiplet release minor --pre rcPipeline steps
Every shiplet release runs these steps in order:
| Step | Description |
| --------------------- | -------------------------------------------------------------------------------- |
| Pre-flight checks | Git repo exists, clean working tree, on main/master branch, package.json present |
| Tests | Runs your test suite inside the container (auto-detects jest/vitest/mocha) |
| Version bump | Updates package.json (and package-lock.json) to the new version |
| Changelog | Generates/prepends CHANGELOG.md from conventional commits since the last tag |
| Git commit + tag | git commit -m "chore(release): vX.Y.Z" and git tag -a vX.Y.Z |
| Image build | Rebuilds your container image tagged with the new version |
| Git push | git push && git push --tags |
| npm publish | (optional, with --publish) Runs npm publish |
Dry run
See exactly what would happen without changing anything:
shiplet release minor --dry-runOutput includes a full changelog preview, version diff, and step-by-step simulation.
Flags reference
| Flag | Description |
| ---------------- | ---------------------------------------------- |
| --dry-run | Simulate without mutations |
| --yes | Skip confirmation prompt |
| --force | Skip branch + clean-tree enforcement |
| --pre <tag> | Add pre-release suffix (alpha, beta, rc) |
| --skip-tests | Skip the test suite |
| --skip-build | Skip container image rebuild |
| --skip-push | Skip git push |
| --publish | Also run npm publish |
| --access <lvl> | npm publish access: public or restricted |
Conventional commits
The changelog generator parses Conventional Commits:
feat(auth): add OAuth2 login → 🚀 Features
fix(db): handle null result → 🐛 Bug Fixes
perf(cache): use LRU eviction → ⚡ Performance
refactor(api): simplify middleware → ♻️ Refactoring
docs: update README → 📝 Documentation
feat!: redesign public API → 💥 Breaking ChangesNon-conventional commits are grouped under 📌 Other.
Container Health Dashboard
# One-shot health view
shiplet health
# Auto-refresh every 3 seconds
shiplet health --watchDisplays a live table with per-service:
- Status (running/starting/unhealthy — colour-coded)
- CPU % (green < 40%, yellow < 80%, red > 80%)
- Memory usage (current / limit)
- Port mappings
Volume Snapshots
Back up and restore named Docker/Podman volumes at any time — useful before destructive migrations or sharing a dev database state with a colleague.
# Save a named snapshot of all volumes
shiplet snapshot save before-migration
# List all snapshots
shiplet snapshot list
# Restore a snapshot (interactive picker if name omitted)
shiplet snapshot restore before-migration
# Delete a snapshot
shiplet snapshot delete before-migrationSnapshots are stored in .shiplet/snapshots/ as compressed tarballs. Each volume gets its own file: <snapshot-name>-<volume-name>.tar.gz.
Linting
# Run all detected linters
shiplet lint
# Run linters and auto-fix where possible
shiplet lint --fixShiplet inspects your package.json and config files to detect and run:
| Tool | Config files detected |
| -------------- | ------------------------------------- |
| Biome | biome.json, biome.jsonc |
| OXLint | devDependencies.oxlint |
| ESLint | .eslintrc*, eslint.config.* |
| Prettier | .prettierrc*, prettier.config.* |
| TypeScript | tsconfig.json (runs tsc --noEmit) |
All linters run inside the container so the environment is consistent with CI.
Scaling Services
# Scale the app service to 3 replicas
shiplet scale app=3
# Scale multiple services at once
shiplet scale app=2 worker=4
# Scale back to 1
shiplet scale app=1Uses docker/podman compose up --scale under the hood — containers are added/removed without recreating existing ones.
shiplet.config.json
shiplet stores project-level configuration in shiplet.config.json at the project root (alongside shiplet.yml). This file is safe to commit.
{
"runtime": "podman",
"appName": "my-app",
"nodeVersion": "20",
"packageManager": "pnpm",
"port": 3000
}| Key | Description |
| ---------------- | ---------------------------------------------- |
| runtime | Pinned container runtime: docker or podman |
| appName | Used as the Docker Compose project name |
| nodeVersion | Node.js version inside the app container |
| packageManager | npm, yarn, or pnpm |
| port | Host port the app is exposed on |
Override the runtime at any time without editing the file:
SHIPLET_RUNTIME=docker shiplet upWeb Dashboard
Launch a live web UI to manage all your containers, projects, and configuration:
shiplet dashboard
# or the alias:
shiplet uiOpens http://localhost:6171 automatically.
Dashboard sections
| Section | What it shows |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Overview | Running/stopped containers with live CPU%, memory bars, network I/O, and port mappings. System info panel. |
| Projects | Auto-scanned Shiplet projects. Per-project: services, runtime badge, version, Up/Down/Restart/Build buttons. |
| Containers | All containers (including stopped) with searchable table, per-container start/stop/restart/remove actions. |
| Images | All pulled images with repository, tag, size, and creation date. |
| Volumes | Named volumes with driver and mount path. |
| Logs | Live WebSocket log streaming — select any container, toggle follow mode, clear. |
| Release | Visual release wizard: bump selector, pre-release tag, checkboxes for skip-tests/publish, dry-run preview with commit breakdown. |
| Settings | Docker ↔ Podman runtime switcher. Per-project .env editor. CLI quick-reference grid. |
Options
# Custom port
shiplet dashboard --port 8080
# Don't auto-open browser
shiplet dashboard --no-open
# Or set via env
SHIPLET_UI_PORT=9000 shiplet dashboardLive updates
The dashboard uses WebSockets for real-time data:
- Container stats (CPU, memory, network) refresh every 3 seconds
- Log streaming is live — tail any running container with zero latency
- The green dot in the top bar shows the WebSocket connection status
Examples
Two complete example projects are included in the examples/ directory.
examples/express-docker
A production-ready Express.js REST API using the Docker runtime.
Services: PostgreSQL 16, Redis 7, Mailpit, Adminer
cd examples/express-docker
shiplet up -d
shiplet npm install
shiplet exec app node src/db/migrate.js
# App → http://localhost:3000
# Adminer → http://localhost:8080
# Mailpit → http://localhost:8025Features: request logging (morgan), security headers (helmet), Redis caching with 60s TTL, PostgreSQL connection pooling, full CRUD /api/items.
examples/fastify-podman
A production-ready Fastify REST API (ESM) using the Podman runtime.
Services: MongoDB 7, Redis 7, MinIO (S3), Mailpit, Mongo Express
cd examples/fastify-podman
SHIPLET_RUNTIME=podman shiplet up -d
shiplet npm install
# App → http://localhost:3000
# Swagger UI → http://localhost:3000/docs
# MinIO UI → http://localhost:9001
# Mongo UI → http://localhost:8081Features: Swagger/OpenAPI docs, JWT authentication, Redis-cached paginated queries with tag/text search, S3-compatible file uploads via pre-signed URLs (MinIO), Mongoose models with indexes, graceful shutdown.
Running either example with the other runtime
Both examples work with either runtime — just override:
# Run the Docker example with Podman
cd examples/express-docker
SHIPLET_RUNTIME=podman shiplet up -d
# Run the Podman example with Docker
cd examples/fastify-podman
SHIPLET_RUNTIME=docker shiplet up -dLicense
MIT © Anand Pilania
