How to use mix release's `env.bat` with Windows service?

Hi,

I have an Elixir 1.15.7 application. At first, it was intended to be installed only on Windows. Therefore I configured mix.iex with Windows-specific environment variable ALLUSERSPROFILE in my config provider:

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      ...
      releases: releases(),
    ]
  end

  ...

  defp releases do
    [
      myapp: [
        include_executables_for: [:windows],
        config_providers: [
          {MyApp.Config.TOMLConfigProvider,
           "%ALLUSERSPROFILE%\\MyCompany\\MyApp\\config.toml"}
        ]
      ]
    ]
  end
end

Now, I need to provide releases for linux as well and I first would like to get rid of the Windows-specific environment variable in mix.iex.

I have adapted my config provider to be used like so:

defp releases do
  [
    myapp: [
      include_executables_for: [:unix, :windows],
      config_providers: [
        {MyApp.Config.TOMLConfigProvider,
         {:system, "MYAPP_CONFIG_DIR", "/configuration.toml"}}
      ]
    ]
  ]
end

and I have created a rel/env.bat.eex:

@echo off

set MYAPP_CONFIG_DIR=%ALLUSERSPROFILE%\MyCompany\MyApp

With that in place, I can successfully launch my release with:

C:\MyCompany\MyApp>dir
 Volume in drive C is OS
 Volume Serial Number is ************

 Directory of C:\MyCompany\MyApp

17.01.2024  15:34    <DIR>          .
17.01.2024  15:01    <DIR>          ..
17.01.2024  15:33    <DIR>          bin
17.01.2024  15:33    <DIR>          erts-13.2.2.5
17.01.2024  15:34    <DIR>          lib
17.01.2024  15:34    <DIR>          releases
17.01.2024  15:34           451 643 unins000.dat
17.01.2024  15:33         3 223 613 unins000.exe
               2 File(s)      3 675 256 bytes
               6 Dir(s)  1 530 448 859 136 bytes free

C:\MyCompany\MyApp>bin\myapp.bat start

But the goal is to install the Windows service and after doing so (i.e. bin\myapp.bat install), the problem is that the service stops immediately after it started:

C:\MyCompany\MyApp>erts-13.2.2.5\bin\erlsrv.exe start myapp_myapp
erts-13.2.2.5\bin\erlsrv.exe: Service myapp_myapp started.

I am pretty sure the env.bat is not executed when starting the Windows service. I have no log to confirm this but when I use an absolute path in my config provider instead of the {:system, "MYAPP_CONFIG_DIR", "/configuration.toml"} tuple, then the service starts and I can successfully access my web app.

Is mix release’s env.bat suppose to work with Windows service?

Still the same problem when rel/env.bat.eex contains the final path instead of relying on ALLUSERSPROFILE:

@echo off

set MYAPP_CONFIG_DIR=C:\ProgramData\MyCompany\MyApp

After reviewing bin\myapp.bat and in particular the install part, I understand a bit better how things works.

With the help of erlsrv documentation, I removed completely my rel/env.bat.eex and instead I run the command below after running bin\myapp.bat install:

C:\MyCompany\MyApp>erts-13.2.2.5\bin\erlsrv.exe set myapp_myapp -env MYAPP_CONFIG_DIR=%ALLUSERSPROFILE%\MyCompany\MyApp
erts-13.2.2.5\bin\erlsrv.exe: Service myapp_myapp updated.

This way the Windows service starts successfully.

So the workaround works but that still does not explain why env.bat is not evaluated when starting a Windows service. Any idea?

Can you load the config in Application.start with something like vapor or does it need to be a config provider?

Do you need to set that env var or can you concat the rest of it to ALLUSERPROFILE instead of creating an env var and concatenating onto that?

If you’re open to moving it basedir has some functions to handle common paths.

https://www.erlang.org/doc/man/filename

All I need is to configure the app at runtime with a human-friendly config file (TOML).
I was not aware of vapor. It seems interesting and I will have a deeper look.

I am not sure I understand what you mean here.

Interesting but I did not see a function that could return the path for a typical config file for an application that runs as a Windows service, which should be (to the best of my knowledge, please correct me if I am wrong) %ALLUSERSPROFILE%\MyCompany\MyApp (C:\ProgramData\MyCompany\MyApp on Windows 10 and 11).

Also in the near future, the application will have to run on Linux (NixOS) with systemd, so such a function would have to work with these as well. My systemd unit will use ConfigurationDirectory=myapp which puts config in /etc/myapp.

You might be right. I’m just storing it in the app’s installation directory and using vapor.

Beware there is a bug with erlsrv and you might need to put the service in Delayed Start for it to start after a reboot.

I did notice this a long time ago but I did not have the time to look any further. Do you have a link to this bug?

1 Like