{"id":15171662,"url":"https://github.com/dbcdk/morph","last_synced_at":"2025-05-15T02:00:25.175Z","repository":{"id":33469602,"uuid":"150071528","full_name":"DBCDK/morph","owner":"DBCDK","description":"NixOS deployment tool","archived":false,"fork":false,"pushed_at":"2025-05-07T09:01:08.000Z","size":365,"stargazers_count":914,"open_issues_count":70,"forks_count":66,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-05-07T09:23:40.772Z","etag":null,"topics":["deployment","devops","nix","nixos","renovate-enabled"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DBCDK.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-09-24T08:05:12.000Z","updated_at":"2025-05-06T04:06:29.000Z","dependencies_parsed_at":"2024-03-11T13:44:18.411Z","dependency_job_id":"4b2e8f3f-2b0e-4766-8e9f-f5a21fb09d6c","html_url":"https://github.com/DBCDK/morph","commit_stats":{"total_commits":254,"total_committers":34,"mean_commits":7.470588235294118,"dds":0.6377952755905512,"last_synced_commit":"b0d1fd1c5963ea2d7ff65f8f77172567def7aed0"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DBCDK%2Fmorph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DBCDK%2Fmorph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DBCDK%2Fmorph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DBCDK%2Fmorph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DBCDK","download_url":"https://codeload.github.com/DBCDK/morph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259363,"owners_count":22040815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deployment","devops","nix","nixos","renovate-enabled"],"created_at":"2024-09-27T09:01:46.786Z","updated_at":"2025-05-15T02:00:25.122Z","avatar_url":"https://github.com/DBCDK.png","language":"Go","readme":"# morph\n\n[![Flake Checks](https://github.com/DBCDK/morph/actions/workflows/checks.yml/badge.svg?branch=master)](https://github.com/DBCDK/morph/actions/workflows/build.yaml)\n[![Example Configs](https://github.com/DBCDK/morph/actions/workflows/build-test.yaml/badge.svg?branch=master)](https://github.com/DBCDK/morph/actions/workflows/build.yaml)\n\nMorph is a tool for managing existing NixOS hosts - basically a fancy wrapper around `nix-build`, `nix copy`, `nix-env`, `/nix/store/.../bin/switch-to-configuration`, `scp` and more.\nMorph supports updating multiple hosts in a row, and with support for health checks makes it fairly safe to do so.\n\n\n## Notable features\n\n* multi host support\n* health checks\n* no state\n\n\n## Installation and prerequisites\n\nMorph requires `nix` (at least v2), `ssh` and `scp` to be available on `$PATH`.\nIt should work on any modern Linux distribution, but NixOS is the only one we test on.\n\nPre-built binaries are not provided, since we install morph through an overlay.\n\nThe easiest way to get morph up and running is to fork this repository and run `nix-build`, which should result in a store path containing the morph binary.\nConsider checking out a specific tag, or at least pin the version of morph you're using somehow.\n\n\n## Using morph\n\nAll commands support a `--help` flag; `morph --help` as of v1.0.0:\n```\n$ morph --help\nusage: morph [\u003cflags\u003e] \u003ccommand\u003e [\u003cargs\u003e ...]\n\nNixOS host manager\n\nFlags:\n  --help     Show context-sensitive help (also try --help-long and --help-man).\n  --version  Show application version.\n  --dry-run  Don't do anything, just eval and print changes\n\nCommands:\n  help [\u003ccommand\u003e...]\n    Show help.\n\n  build [\u003cflags\u003e] \u003cdeployment\u003e\n    Evaluate and build deployment configuration to the local Nix store\n\n  push [\u003cflags\u003e] \u003cdeployment\u003e\n    Build and transfer items from the local Nix store to target machines\n\n  deploy [\u003cflags\u003e] \u003cdeployment\u003e \u003cswitch-action\u003e\n    Build, push and activate new configuration on machines according to switch-action\n\n  check-health [\u003cflags\u003e] \u003cdeployment\u003e\n    Run health checks\n\n  upload-secrets [\u003cflags\u003e] \u003cdeployment\u003e\n    Upload secrets\n\n  exec [\u003cflags\u003e] \u003cdeployment\u003e \u003ccommand\u003e...\n    Execute arbitrary commands on machines\n```\n\nNotably, `morph deploy` requires a `\u003cswitch-action\u003e`.\nThe switch-action must be one of `dry-activate`, `test`, `switch` or `boot` corresponding to `nixos-rebuild` arguments of the same name.\nRefer to the [NixOS manual](https://nixos.org/nixos/manual/index.html#sec-changing-config) for a detailed description of switch-actions.\n\nFor help on this and other commands, run `morph \u003ccmd\u003e --help`.\n\nExample deployments can be found in the `examples` directory, and built as follows:\n```\n$ morph build examples/simple.nix\nSelected 2/2 hosts (name filter:-0, limits:-0):\n\t  0: db01 (secrets: 0, health checks: 0)\n\t  1: web01 (secrets: 0, health checks: 0)\n\n\u003cprobably lots of nix-build output\u003e\n\n/nix/store/grvny5ga2i6jdxjjbh2ipdz7h50swi1n-morph\nnix result path:\n/nix/store/grvny5ga2i6jdxjjbh2ipdz7h50swi1n-morph\n```\n\nThe result path is written twice, which is a bit silly, but the reason is that only the result path is written to stdout, and everything else (including `nix-build` output) is redirected to stderr.\nThis makes it easy to use morph for scripting, e.g. if one want to build using morph and then `nix copy` the result path somewhere else.\n\nNote that `examples/simple.nix` contain two different hosts definitions, and a lot of copy paste.\nAll the usual nix tricks can of course be used to avoid duplication.\n\nHosts can be deployed with the `deploy` command as follows:\n`morph deploy examples/simple.nix` (this will fail without modifying `examples/simple.nix`).\n\n\n### Selecting/filtering hosts to build and deploy\n\nAll hosts defined in a deployment file is returned to morph as a list of hosts, which can be manipulated with the following flags:\n\n- `--on glob` can be used to select hosts by name, with support for glob patterns\n- `--limit n` puts an upper limit on the number of hosts\n- `--skip n` ignore the first `n` hosts\n- `--every n` selects every n'th host, useful for e.g. selecting all even (or odd) numbered hosts\n\n(all relevant commands should already support these flags.)\n\nThe ordering currently can't be changed, but should be deterministic because of nix.\n\nMost commands output a header like this:\n```\nSelected 4/17 hosts (name filter:-6, limits:-7):\n\t  0: foo-p02 (secrets: 0, health checks: 1)\n\t  1: foo-p05 (secrets: 0, health checks: 1)\n\t  2: foo-p08 (secrets: 0, health checks: 1)\n\t  3: foo-p11 (secrets: 0, health checks: 1)\n```\n\nThe output is pretty self explanatory, except probably for the last bit of the first line.\n`name filter` shows the change in number of hosts after glob matching on the hosts name, and `limits` shows the change after applying `--limit`, `--skip` and `--every`.\n\n\n#### Tagging hosts\n\nEach host can be tagged with an arbitrary amount of tags, which can be used to select and sort hosts.\n\nTo tag a host, use the `deployment.tags` option, e.g. `deployment.tags = [ \"prod\" \"master\" \"rack-17\" ]`. Hosts can now be selected with the `--tagged` option, e.g.`--tagged=\"prod,master\"` will only select hosts tagged _both_ `prod` _and_ `master`.\n\nTo sort hosts based on tags, use the `network.ordering.tags` option, e.g. `network.ordering.tags = [ \"master\" \"slave\"]`. This ordering can be changed at runtime using the `--order-by-tags` option, eg. `--order-by-tags=\"slave,master\"` (this also works when `network.ordering.tags` isn't defined). Hosts without matching tags will end up at the end of the list.\n\n\n### Environment Variables\n\nMorph supports the following (optional) environment variables:\n\n- `SSH_IDENTITY_FILE` the (local) path to the SSH private key file that should be used\n- `SSH_USER` specifies the user that should be used to connect to the remote system\n- `SSH_SKIP_HOST_KEY_CHECK` if set disables host key verification\n- `SSH_CONFIG_FILE` allows to change the location of the ~/.ssh/config file\n- `MORPH_NIX_EVAL_CMD` morph will invoke this command instead of default: \"nix-instantiate\" on PATH \n- `MORPH_NIX_BUILD_CMD` morph will invoke this command instead of default: \"nix-build\" on PATH \n- `MORPH_NIX_SHELL_CMD` morph will invoke this command instead of default: \"nix-shell\" on PATH\n- `MORPH_NIX_EVAL_MACHINES` path to a custom eval-machines.nix. Defaults to the eval-machines.nix bundled with morph\n\n### Secrets\n\nFiles can be uploaded without ever ending up in the nix store, by specifying each file as a secret. This will use scp for copying a local file to the remote host.\n\nSee `examples/secrets.nix` or the type definitions in `data/options.nix`.\n\nTo upload secrets, use the `morph upload-secrets` subcommand, or pass `--upload-secrets` to `morph deploy`.\n\n*Note:*\nMorph will automatically create directories parent to `secret.Destination` if they don't exist.\nNew dirs will be owned by root:root and have mode 755 (drwxr-xr-x).\nAutomatic directory creation can be disabled by setting `secret.mkDirs = false`.\n\n\n### Health checks\n\nMorph has support for two types of health checks:\n\n* command based health checks, which are run on the target host (success defined as exit code == 0)\n* HTTP based health checks, which are run from the host Morph is running on (success defined as HTTP response codes in the 2xx range)\n\nSee `examples/healthchecks.nix` for an example.\n\nThere are no guarantees about the order health checks are run in, so if you need something complex you should write a script for it (e.g. using `pkgs.writeScript`).\nHealth checks will be repeated until success, and the interval can be configured with the `period` option (see `data/options.nix` for details).\n\nIt is currently possible to have expressions like `\"test \\\"$(systemctl list-units --failed --no-legend --no-pager |wc -l)\\\" -eq 0\"` (count number of failed systemd units, fail if non-zero) as the first argument in a cmd-healthcheck. This works, but is discouraged, and might break at any time.\n\n\n### Pre-deploy checks (experimental)\n\nMorph supports running checks before changing the target host (note: files will still be pushed to the host).\nThese checks work exactly like health checks, which means they will run forever until they have all succeeded.\nThis is an _experimental feature that is very likely to change_ in the future. Comments and feedback welcome :).\n\nPre-deploy checks can be defined using `deployment.preDeployChecks`.\n\n\n### Advanced configuration\n\n**nix.conf-options:** The \"network\"-attrset supports a sub-attrset named \"nixConfig\". Options configured here will pass `--option \u003cname\u003e \u003cvalue\u003e` to all nix commands.\nNote: these options apply to an entire deployment and are *not* configurable on per-host basis.\nThe default is an empty set, meaning that the nix configuration is inherited from the build environment. See `man nix.conf`.\n\n**network.buildShell**\nBy passing `--allow-build-shell` and setting `network.buildShell` to a nix-shell compatible derivation (eg. `pkgs.mkShell ...`), it's possible to make morph execute builds from within the defined shell. This makes it possible to have arbitrary dependencies available during the build, say for use with nix build hooks. Be aware that the shell can potentially execute any command on the local system.\n\n**special deployment options:**\n\n(per-host granularity)\n\n`buildOnly` makes morph skip the \"push\" and \"switch\" steps for the given host, even if \"morph deploy\" or \"morph push\" is executed. (default: false)\n\n`substituteOnDestination` Sets the `--substitute-on-destination` flag on nix copy, allowing for the deployment target to use substitutes. See `nix copy --help`. (default: false)\n\n\nExample usage of `nixConfig` and deployment module options:\n```\nnetwork = {\n    nixConfig = {\n        \"extra-sandbox-paths\" = \"/foo/bar\";\n    };\n};\n\nmachine1 = { ... }: {\n    deployment.buildOnly = true;\n};\n\nmachine2 = { ... }: {\n    deployment.substituteOnDestination = true;\n};\n```\n\n**mutually recursive configurations**\nEach host's configuration has access to a `nodes` argument, which contains the compiled configurations of all hosts.\n\n```\nmachine1 = { nodes, ... }: {\n    hostnames.machine2 = \n        (builtins.head nodes.machine2.networking.interfaces.foo.ipv4.addresses).address;\n    networking.interfaces.foo.ipv4.addresses = [\n        {\n            address = \"10.0.0.10\";\n            prefixLength = 32;\n        }\n    ];\n}\n\nmachine2 = { nodes, ... }: {\n    hostnames.machine1 = \n        (builtins.head nodes.machine1.networking.interfaces.foo.ipv4.addresses).address;\n    networking.interfaces.foo.ipv4.addresses = [\n        {\n            address = \"10.0.0.20\";\n            prefixLength = 32;\n        }\n    ];\n}\n```\n\n\n## Hacking morph\n\nAll commands mentioned below is available in the nix-shell, if you run `nix-shell` with working dir = project root. The included `shell.nix` uses the latest `nixos-unstable` from GitHub by default, but you can override this by passing in another, eg. `nix-shell --arg nixpkgs '\u003cnixpkgs\u003e'` for your `$NIX_PATH` nixpkgs.\n\n### Go dependency management\n\nFrom within `nix-shell`, `go get -u` updates all go modules. Remember to update the `vendorSha256` in `./default.nix`\n\n### Building the project with pinned dependencies\n\n$ `nix-build --arg nixpkgs \"builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/\u003crev\u003e.tar.gz\"`\n\n## About the project\n\nWe needed a tool for managing our NixOS servers, and ended up writing one ourself. This is it. We use it on a daily basis to build and deploy our NixOS fleet, and when we need a feature we add it.\n\nMorph is by no means done. The CLI UI might (and probably will) change once in a while.\nThe code is written by humans with an itch to scratch, and we're discussing a complete rewrite (so feel free to complain about the source code since we don't like it either).\nIt probably wont accidentally switch your local machine, so you should totally try it out, but do consider pinning to a specific git revision.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbcdk%2Fmorph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdbcdk%2Fmorph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbcdk%2Fmorph/lists"}