Skip to content

v1.2.0

Compare
Choose a tag to compare
@mpscholten mpscholten released this 15 Nov 01:56

IHP is a modern batteries-included haskell web framework, built on top of Haskell and Nix. Blazing fast, secure, easy to refactor and the best developer experience with everything you need - from prototype to production.

This release brings a new out of the box deployment process based on nixos-rebuild, a new way to get docker images for an IHP app and much more.

Major Changes

  • Deployment with deploy-to-nixos:
    You can now easily deploy IHP apps to any NixOS server that is reachable via SSH. E.g. if you start a new AWS EC2 instance with an NixOS image and your SSH key, you can now deploy the IHP app, including database and migrations within minutes.

    The flake.nix can export a full NixOS configuration like this:

    {
        inputs = {
            ihp.url = "github:digitallyinduced/ihp/v1.2";
            nixpkgs.follows = "ihp/nixpkgs";
            flake-parts.follows = "ihp/flake-parts";
            devenv.follows = "ihp/devenv";
            systems.follows = "ihp/systems";
        };
    
        outputs = inputs@{ ihp, flake-parts, systems, ... }:
            flake-parts.lib.mkFlake { inherit inputs; } {
    
                systems = import systems;
                imports = [ ihp.flakeModules.default ];
    
                perSystem = { pkgs, ... }: {
                    ihp = {
                        enable = true;
                        projectPath = ./.;
                        packages = with pkgs; [
                            # Native dependencies, e.g. imagemagick
                            nodejs
                        ];
                        haskellPackages = p: with p; [
                            # Haskell dependencies go here
                            p.ihp
                            cabal-install
                            base
                            wai
                            text
                            hlint
    
                            http-streams
                            ihp-stripe
                            ihp-oauth-google
                            retry
                        ];
                    };
                };
    
                flake.nixosConfigurations."staging.example.com" = nixpkgs.lib.nixosSystem {
                    system = "x86_64-linux";
                    specialArgs = inputs;
                    modules = [
                        "${nixpkgs}/nixos/modules/virtualisation/amazon-image.nix"
                        ihp.nixosModules.appWithPostgres
                        ({ ... }: {
                            security.acme.defaults.email = "me@example.com";
    
                            services.ihp = {
                                domain = "myihpapp.com";
                                migrations = ./Application/Migration;
                                schema = ./Application/Schema.sql;
                                fixtures = ./Application/Fixtures.sql;
                                sessionSecret = "xxx";
                                additionalEnvVars = {
                                    GHCRTS = "-A32M -N2";
                                };
                            };
    
                            # This should reflect the nixos version from the NixOS AMI initally installed
                            # After the initial install, it should not be changed. Otherwise e.g. the postgres
                            # server might need a manual data migration if NixOS changes the default postgres version
                            system.stateVersion = "23.05";
    
                            # Optional Example: Email on App Crash
                            systemd.services.app.onFailure = [ "notify-email@%n.service" ];
                            systemd.services.worker.onFailure = [ "notify-email@%n.service" ];
    
                            programs.msmtp = {
                                enable = true;
                                defaults = {
                                    tls = true;
                                    port = 587;
                                };
                                accounts = {
                                    default = {
                                        auth = true;
                                        from = "monitoring@digitallyinduced.com";
                                        host = "email-smtp.eu-west-1.amazonaws.com";
                                        user = "XXXXXXXX";
                                        passwordeval = "echo 'XXXXXXXX'";
                                    };
                                };
                            };
                            systemd.services."notify-email@" = {
                                serviceConfig.Type = "oneshot";
                                path = with pkgs; [ systemd system-sendmail ];
                                scriptArgs = "%I";
                                script = ''
                                    UNIT=$(systemd-escape $1)
                                    TO="monitoring@digitallyinduced.com"
                                    SUBJECT="$UNIT Failed"
                                    HEADERS="To:$TO\nSubject: $SUBJECT\n"
                                    BODY=$(systemctl status --no-pager $UNIT || true)
                                    echo -e "$HEADERS\n$BODY" | sendmail -t
                                '';
                            };
    
                            # Optional Example: Run an IHP script every 30mins
                            systemd.services.monitorCampaigns = {
                                serviceConfig = {
                                    Type = "oneshot";
                                    WorkingDirectory = "${ihpApp}/lib";
                                    ExecStart = "${ihpApp}/bin/MonitorCampaigns";
                                };
                                environment = config.systemd.services.app.environment;
                                onFailure = [ "notify-email@%n.service" ];
                            };
                            systemd.timers.monitorCampaignsEvery30Mins = {
                                wantedBy = [ "timers.target" ];
                                partOf = [ "monitorCampaigns.service" ];
                                timerConfig = {
                                    OnCalendar = "*-*-* *:30:00";
                                    Unit = "monitorCampaigns.service";
                                };
                            };
                        })
                    ];
                };
    
            };
    }

    Assuming your NixOS server can be conneted to via ssh staging.example.com, you can now run this:

    deploy-to-nixos staging.example.com

    This command will build your app, create necessary systemd services for the app and any IHP workers, install postgres and fill it up with your app's Schema.sql and Fixtures.sql and registers a migrate command on the server that runs the latest database migrations. It also puts a nginx with letsencrypt in front to handle HTTPS requests out of the box.

    If you use an external postgres (this is likely the case for most serious production deployments), use ihp.nixosModules.app instead of ihp.nixosModules.appWithPostgres.

    This will now apply the full above NixOS configuration to the server. Internally this tool is a wrapper around nixos-rebuild. E.g. the above call with result in:

    nixos-rebuild switch -j auto --use-substitutes --fast --flake .#staging.example.com --target-host staging.example.com --build-host staging.example.com --option substituters https://digitallyinduced.cachix.org --option trusted-public-keys digitallyinduced.cachix.org:digitallyinduced.cachix.org-1:y+wQvrnxQ+PdEsCt91rmvv39qRCYzEgGQaldK26hCKE=
    ssh staging.example.com systemctl start migrate

    If you e.g. want to build the binaries on a different server than your runtime server, you can call nixos-rebuild directly instead of using the deploy-to-nixos wrapper.

    IHP now ships serveral NixOS modules that you can use to compose your IHP NixOS infrastructure.

    Check out the docs for more information.

  • Docker Images:
    You can now build docker images from your IHP apps with ease:

    # Faster build times, but unoptimized GHC binaries
    nix build .#unoptimized-docker-image
    
    # Slow build times, but optimized GHC binaries
    nix build .#optimized-docker-image
  • Support HSX expressions like <input value={project.id}/>

    You can now use IHP UUIDs/ID values like user ID or project ID in HSX attributes:

    -- Previous:
    <input value={inputValue project.id}/>
    
    -- New:
    <input value={project.id}/>

Minor Changes

Full Changelog: v1.0.1...v1.1.0

Feature Voting

Help decide what's coming next to IHP by using the Feature Voting!

Updating

→ See the UPGRADE.md for upgrade instructions.


If you have any problems with updating, let us know on the IHP Discourse.

📧 To stay in the loop, subscribe to the IHP release emails (right at the bottom of the page). Or follow digitally induced on twitter.