├── .gitignore ├── .vscode └── settings.json ├── README.yaml ├── fly.nix ├── flake.lock ├── README.md ├── app.nix ├── flake.nix └── .github └── workflows └── fly.yml /.gitignore: -------------------------------------------------------------------------------- 1 | result -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.tabSize": 2 4 | } -------------------------------------------------------------------------------- /README.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | title: Build with Nix, deploy on Fly 3 | author: by Piper McCorkle at Lutris 4 | --- -------------------------------------------------------------------------------- /fly.nix: -------------------------------------------------------------------------------- 1 | let 2 | base = { 3 | kill_signal = "SIGINT"; 4 | kill_timeout = 5; 5 | processes = [ ]; 6 | services = [ 7 | { 8 | internal_port = 8080; 9 | processes = [ "app" ]; 10 | protocol = "tcp"; 11 | concurrency = { 12 | hard_limit = 25; 13 | soft_limit = 20; 14 | type = "connections"; 15 | }; 16 | ports = [ 17 | { 18 | handlers = [ "http" ]; 19 | port = 80; 20 | } 21 | { 22 | handlers = [ "tls" "http" ]; 23 | port = 443; 24 | } 25 | ]; 26 | http_checks = [ 27 | { 28 | grace_period = "1s"; 29 | interval = "15s"; 30 | timeout = "2s"; 31 | path = "/"; 32 | method = "get"; 33 | protocol = "http"; 34 | } 35 | ]; 36 | } 37 | ]; 38 | }; 39 | in 40 | { 41 | production = base // { 42 | app = "nix-fly-template"; 43 | }; 44 | staging = base // { 45 | app = "nix-fly-template-staging"; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1631561581, 6 | "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1634515797, 21 | "narHash": "sha256-elgCUC2khtBkOSpE4gDymNvthTZAI4hGI2iNu3YEUkA=", 22 | "owner": "nixos", 23 | "repo": "nixpkgs", 24 | "rev": "5f0194220f2402b06f7f79bba6351895facb5acb", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "nixos", 29 | "ref": "nixos-unstable", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "flake-utils": "flake-utils", 37 | "nixpkgs": "nixpkgs" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [nix-fly-template](https://github.com/LutrisEng/nix-fly-template) 2 | 3 | [![Deploy](https://github.com/LutrisEng/nix-fly-template/actions/workflows/fly.yml/badge.svg)](https://github.com/LutrisEng/nix-fly-template/actions/workflows/fly.yml) 4 | 5 | This is a barebones template to build an application with [Nix](https://nixos.org/) and deploy it on [Fly.io](https://fly.io/). 6 | 7 | You can take a look at this barebones application serving this README.md on two environments: 8 | 9 | - Staging: [https://nix-fly-template-staging.fly.dev](https://nix-fly-template-staging.fly.dev) 10 | - Production: [https://nix-fly-template.fly.dev](https://nix-fly-template.fly.dev) 11 | 12 | ## How to use 13 | 14 | 1. Use this template to create a repository for your app 15 | 2. Launch a Fly app for each environment you want (prod, staging, etc.) 16 | 3. Add your Fly apps' names in `fly.nix`, and configure `.github/workflows/fly.yml` to deploy to your apps 17 | 4. Add a `FLY_API_TOKEN` secret to your GitHub repo containing a Fly API token 18 | 5. Modify `app.nix` and `flake.nix` to build your app and Docker container however you want 19 | 20 | GitHub Actions will deploy to staging then production on every push to the main branch. 21 | -------------------------------------------------------------------------------- /app.nix: -------------------------------------------------------------------------------- 1 | # This is where you define the derivation for your app. 2 | # For this example, we'll build a basic website using nginx. 3 | # Based on https://github.com/NixOS/nixpkgs/blob/5f33ded6018c9bd2e203cd6d7bad4d6a62e46c2f/pkgs/build-support/docker/examples.nix#L44 4 | { stdenv, pandoc, writeText, writeShellScriptBin, nginx }: 5 | let 6 | root = stdenv.mkDerivation { 7 | name = "app-static"; 8 | src = ./.; 9 | buildInputs = [ 10 | pandoc 11 | ]; 12 | buildPhase = '' 13 | mkdir root 14 | pandoc README.md README.yaml -s -o root/index.html 15 | ''; 16 | installPhase = '' 17 | mkdir $out 18 | cp -R root/* $out/ 19 | ''; 20 | }; 21 | conf = writeText "nginx.conf" '' 22 | user nobody nobody; 23 | daemon off; 24 | error_log /dev/stdout info; 25 | pid /dev/null; 26 | events {} 27 | http { 28 | access_log /dev/stdout; 29 | server { 30 | listen 8080; 31 | index index.html; 32 | add_header X-Root-Path ${root}; 33 | location / { 34 | root ${root}; 35 | } 36 | } 37 | } 38 | ''; 39 | in 40 | writeShellScriptBin "app" '' 41 | echo Starting nginx 42 | ${nginx}/bin/nginx -c ${conf} 43 | echo nginx exited with code $? 44 | exit $? 45 | '' 46 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A template for building and deploying applications on fly.com"; 3 | 4 | inputs = { 5 | nixpkgs.url = github:nixos/nixpkgs/nixos-unstable; 6 | flake-utils.url = github:numtide/flake-utils; 7 | }; 8 | 9 | outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: 10 | let 11 | pkgs = nixpkgs.legacyPackages.${system}; 12 | in 13 | rec { 14 | packages.app = pkgs.callPackage ./app.nix { }; 15 | defaultPackage = packages.app; 16 | 17 | flyConfigs = import ./fly.nix; 18 | 19 | apps = 20 | let 21 | deployers = nixpkgs.lib.mapAttrs' 22 | (name: config: 23 | nixpkgs.lib.nameValuePair "deploy-${name}" 24 | (flake-utils.lib.mkApp { 25 | drv = pkgs.writeShellScriptBin "deploy-${name}" '' 26 | set -euxo pipefail 27 | export PATH="${nixpkgs.lib.makeBinPath [(pkgs.docker.override { clientOnly = true; }) pkgs.flyctl]}:$PATH" 28 | archive=${self.defaultDockerContainer.x86_64-linux} 29 | config=${(pkgs.formats.toml {}).generate "fly.toml" config} 30 | 31 | image=$(docker load < $archive | awk '{ print $3; }') 32 | flyctl deploy -c $config -i $image 33 | ''; 34 | })) 35 | flyConfigs; 36 | in 37 | { 38 | app = flake-utils.lib.mkApp { 39 | drv = packages.app; 40 | }; 41 | } // deployers; 42 | defaultApp = apps.app; 43 | 44 | # Adjust this to make sense for your app 45 | # Based on https://github.com/NixOS/nixpkgs/blob/5f33ded6018c9bd2e203cd6d7bad4d6a62e46c2f/pkgs/build-support/docker/examples.nix#L44 46 | dockerContainers.app = pkgs.dockerTools.buildLayeredImage { 47 | name = "app"; 48 | contents = [ 49 | pkgs.dockerTools.fakeNss 50 | packages.app 51 | ]; 52 | extraCommands = '' 53 | mkdir -p var/{log,cache}/nginx 54 | ''; 55 | config = { 56 | Cmd = [ apps.app.program ]; 57 | ExposedPorts = { 58 | "8080/tcp" = { }; 59 | }; 60 | }; 61 | }; 62 | defaultDockerContainer = dockerContainers.app; 63 | 64 | devShell = pkgs.mkShell { 65 | buildInputs = [ 66 | pkgs.flyctl 67 | pkgs.pandoc 68 | ]; 69 | }; 70 | 71 | hydraJobs = { 72 | inherit dockerContainers packages; 73 | }; 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /.github/workflows/fly.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: cachix/install-nix-action@v13 12 | with: 13 | install_url: https://nixos-nix-install-tests.cachix.org/serve/5almdggn9vygxkx6wpsxbv1h4z5p2qjj/install 14 | install_options: --tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve 15 | extra_nix_config: | 16 | experimental-features = nix-command flakes 17 | - uses: cachix/cachix-action@v10 18 | with: 19 | name: nix-fly-template 20 | authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} 21 | - run: nix --log-format raw -L build .#defaultDockerContainer.x86_64-linux 22 | staging: 23 | needs: build 24 | runs-on: ubuntu-latest 25 | concurrency: staging 26 | environment: 27 | name: staging 28 | url: https://nix-fly-template-staging.fly.dev/ 29 | steps: 30 | - uses: actions/checkout@v2 31 | - uses: cachix/install-nix-action@v13 32 | with: 33 | install_url: https://nixos-nix-install-tests.cachix.org/serve/5almdggn9vygxkx6wpsxbv1h4z5p2qjj/install 34 | install_options: --tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve 35 | extra_nix_config: | 36 | experimental-features = nix-command flakes 37 | - uses: cachix/cachix-action@v10 38 | with: 39 | name: nix-fly-template 40 | authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} 41 | - run: nix --log-format raw -L run .#deploy-staging 42 | env: 43 | FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} 44 | production: 45 | needs: [ build, staging ] 46 | runs-on: ubuntu-latest 47 | concurrency: production 48 | environment: 49 | name: production 50 | url: https://nix-fly-template.fly.dev/ 51 | steps: 52 | - uses: actions/checkout@v2 53 | - uses: cachix/install-nix-action@v13 54 | with: 55 | install_url: https://nixos-nix-install-tests.cachix.org/serve/5almdggn9vygxkx6wpsxbv1h4z5p2qjj/install 56 | install_options: --tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve 57 | extra_nix_config: | 58 | experimental-features = nix-command flakes 59 | - uses: cachix/cachix-action@v10 60 | with: 61 | name: nix-fly-template 62 | authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} 63 | - run: nix --log-format raw -L run .#deploy-production 64 | env: 65 | FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} --------------------------------------------------------------------------------