Yann Autissier 90bb97cca8 replace the linux-only firewall patching with a portable exposure model
The catalogue publishes 51 compose files with the bare form 'ports: [80]',
which binds a random host port on 0.0.0.0: every service answers the internet.
ufw-docker existed to take that back afterwards, as root, on linux only,
because docker writes its own firewall rules and ufw never sees those ports.

Publishing where you mean to solves it at the source. Verified against the
daemon: '- 80' gives 0.0.0.0:32768, '127.0.0.1::80' gives 127.0.0.1:32769.
Same on macOS and on linux, no privilege, and visible in docker ps.

A stack binds with ${MYOS_BIND_PRIVATE|PUBLIC|MESH} and declares what it
means with <PREFIX>_SERVICE[_<port>]_EXPOSE. myos expose reads the resolved
compose configuration and reports what would be opened; --strict fails when a
port faces the world without declaring it, which is what an agent runs against
a server it did not set up.
2026-09-05 13:07:13 +02:00
2025-12-18 00:47:53 +01:00
wip
2021-07-14 20:38:30 +01:00

myos - Make Your Own Stack

myos runs docker compose stacks: on a server, in a project directory, for a user. It is a thin layer over docker compose that resolves which compose files to load, under which project name, with which environment variables.

The framework itself ships no stack. Ready-to-use stacks (consul, fabio, registrator, postgres, supabase, drone, …) live in a separate catalogue, myos-stacks.

Disclaimer

This is beta software, use it at your own risks.

Requirements

docker (with the docker compose plugin >= 2.24.4, or a docker-compose binary), git and make.

Install

As a command

sudo git clone https://github.com/aya/myos /usr/local/lib/myos
sudo ln -s /usr/local/lib/myos/myos /usr/local/bin/myos

Optionally pin per-machine settings in /etc/conf.d/myos (or /etc/default/myos), one VAR=value per line, no comments:

DOMAIN=example.org
ENV=master

myos runs from the current directory: it passes it as WORKDIR, so stacks and .env are looked up there. A WORKDIR set in the config file wins over the current directory, which pins a machine to its deployment directory.

As a make include

Add to your project Makefile:

MYOS ?= /usr/local/lib/myos
-include $(MYOS)/make/include.mk

Then make help lists the available targets.

Stack catalogue

myos looks for stacks in ./stack, ../stack, ~/.local/share/myos/stack, /usr/local/share/myos/stack and /usr/share/myos/stack:

sudo git clone https://github.com/aya/myos-stacks /usr/local/share/myos

Usage

myos doctor                      # check the installation first
myos ls                          # what stacks are reachable
myos -n up host                  # print what it would run
myos up                          # the stack of the current directory
myos up STACK=host               # a group of stacks, see stack/host/host.mk
myos up STACK=host/fabio         # a single stack
myos up STACK=postgres:9.6       # a versioned stack
myos ps
myos logs
myos config                      # rendered compose file
myos down
myos shutdown                    # every stack: app, host and user

How a stack is resolved

A stack is a directory of compose files. For STACK=<name> in environment ENV, myos loads, in order, whichever of these exist:

<name>.yml   <name>.<ENV>.yml   <ENV>/<name>.yml
<name>.<suffix>.yml   <name>.<suffix>.<ENV>.yml   <name>.<version>.yml

<suffix> comes from the COMPOSE_FILE_* variables that are not false (app, labels, networks, ssh, volumes by default; add e.g. COMPOSE_FILE_WWW=true to also load <name>.www.yml). The framework always appends its own share/compose/networks.yml.

Project names and networks

stack compose project meaning
host/* $(HOSTNAME) one instance per machine: ports 80/443, consul, certbot
User/* user identity one instance per user
anything else <user>-<app>-<env> many instances per machine

Networks: default = _<project> (private to the project), private = <user>-<env> and public = <hostname>, both external and created on demand.

Variables

variable effect
DEBUG=true show executed commands
DRYRUN=true print commands instead of running them
VERBOSE=true show called functions
ENV=<env> environment: selects .env.<env> and the <name>.<env>.yml overlays
STACK=<refs> stacks to act on
SERVICE=<name> target one compose service (exec, run, logs, scale)
MYOS_PROJECT_FORMAT user-env-app (default) or user-app-env for deployments made before myos 2.0
myos env COMPOSE_FILE             # show a variable
myos env COMPOSE_PROJECT_NAME
myos config <stack>               # the rendered compose file

The make targets keep working: print-VAR, stack-<stack>-<command>, <command>@<env>, and a project Makefile that includes make/include.mk.

SETUP_UFW=true enables the ufw/ufw-docker integration (myos setup-ufw).

With make

A project that would rather drive make can include the shim, which turns every myos command into a make target while leaving its own targets alone:

MYOS ?= /usr/local/lib/myos
include $(MYOS)/share/make/shim.mk

make up STACK=host then runs exactly what myos up host runs: the shim only forwards. make is not needed otherwise, and the CLI never calls it.

For agents

skills/myos/SKILL.md is a skill describing how to drive myos, with references on the conventions, the commands and the failure modes. AGENTS.md covers changing myos itself.

Tests

make test           # shellspec: unit + golden (a mocked docker, no daemon needed)
make test-golden    # golden only
make golden-record  # re-record the golden expectations
make lint           # shellcheck

License

GPL-3.0, see LICENSE.

S
Description
common make targets to build and run docker projects
Readme AGPL-3.0
1.2 MiB
Languages
Makefile 85.6%
Shell 10.5%
Dockerfile 3.9%