Yann Autissier f429b8c38d derive the certificates a server needs from what it routes
The hostnames are already declared, once, in the fabio route tags. A
domains.txt would be a second source of truth free to disagree with what is
actually served, so myos cert reads the tags instead and decides on its own
which name needs a wildcard: one is asked for where a tag uses one, and it
absorbs the concrete names it covers. A wildcard covers a single label, so
a.b.example.org keeps its own certificate.

dehydrated issues them: a shell script, no python, which fits a tool that has
to install on any server. It answers http-01 itself on a port bound to the
loopback and routed by fabio, and delegates dns-01 to a provider hook. The
deploy hook writes the two file names fabio watches for, through a temporary
name so fabio never reads half a certificate.

Fixed on the way: the port parser wanted six spaces of indentation and the
catalogue writes four, so every stack that binds its ports was reported
unbound.
2026-09-05 13:41:16 +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%