Skip Navigation

What's the best-practice way to containerise a bot with different environment variables?

I recently asked the best way to run my Lemmy bot on my Synology NAS and most people suggested Docker.

I'm currently trying to get it running on my machine in Docker before transferring it over there, but am running into trouble.

Currently, to run locally, I navigate to the folder and type npm start. That executes tsx src/main.ts.

The first thing main.ts does is access argv to detect if a third argument was given, dev, and if it was, it loads in .env.development, otherwise it loads .env, containing environment variables. It puts those variables into a local variable that I then pass around in the bot. I am definitely not tied to this approach if there is a better practice way of doing it.

Ideally, I would like a way that I can create a Docker image and then run it with either the .env.development variables or the .env ones...maybe even a completely separate one I decide to create after-the-fact.

Right now, I can't even run it. When I type docker-compose up I get npm start: not found.

I assume the current problem is something to do with where stuff is being copied to and what the workdir is, but don't know precisely how to address it.

And once that's resolved, I have even less idea how to go about passing through the environment variables.

Any help would be much appreciated.

You're viewing a single thread.

5 comments
  • Concerning environment variables, you can use volumes in the compose.yaml to bind mount files or directories in your container e.g

     yaml
        
    services:
      node:
        volumes:
          - ./prod.env:/usr/src/app/.env
          - ./dev.env:/usr/src/app/.env.development
    
      

    @elmicha@feddit.org is probably right about the CMD. Read the documentation and learn about the two modes CMD has. Try to figure it out yourself and if you can't, reveal the spoiler below

    Anti Commercial-AI license

    • Yeah I actually originally had it in the ["one", "two"] format, but it was weird because before the arguments I actually wanted the source I got it from had put something along the lines of "sh", "-p" (it was definitely launching into a shell of some sort, with some kind of flag, but I forget exactly what). I removed those, and at the same time removed the square brackets, but messed up by keeping the quote marks.

      Re using volumes, am I understanding this correctly:

      That would mean that when standing up the container from the built Docker image, it contains the two env files, prod and development? Is there, alternatively, an easy way to only have one in the container, and choose which in the docker-compose up command?

      • It's probably "sh" "-c" that was being used. That's the ENTRYPOINT. CMD is passed to the ENTRYPOINT. The docs explain it well, I think.

        As for volumes, TL;DR these are mounted into the container at runtime and are not in the image. You may put the dev and production config in the image, but I'd advise against it, especially if the production config contains secrets. Then they'll be baked into the docker image.

        Finally, parameterising compose files: use environment variables. You can put then into a .env beside your compose.yaml or set them before calling e.g ENVIRONMENT_VARIABLE=someValue docker compose up.

        They are "interpolated" aka used in the compose file like so

         yaml
            
        services:
          web:
            image: "webapp:${TAG}"
        
          

        I recommend you read the docs on the syntax, but for your purpose I'd use something like

         yaml
            
        volumes:
          - ${ENV_FILE:?Env file not provided}:/usr/src/app/.env
        
          

        Then you can ENV_FILE=prod.env docker compose up or put the env var in .env and docker compose will pick it up.

        Anti Commercial-AI license

        • these are mounted into the container at runtime and are not in the image

          Yeah thanks, that's what I thought was the case.

          Personally I think I'd rather not do what you had in the earlier comment, because it feels wrong for the container to know that it's running in dev or prod mode. I like the suggestion of passing in the ENV_FILE that you gave in this comment. But I am struggling a little, because at the moment it seems I need to both put the env filename in the .env itself (which is just silly in its own right) and pass it through with --env-file (or a local environment variable). I can't quite work out why, and the documentation is absolutely no help. I saw some one thread where it was said that with docker-compose, --env-file should set the variable in the compose file, but when I do

           
              
          docker-compose --env-file .env up
          
            

          I get required variable ENV_FILE is missing a value: Env file not provided which goes away if I put the seemingly-redundant ENV_FILE variable in the .env file.

          edit: Worked it out. It seems the issue was my compose.yaml environment variables were in the format - COMMUNITY_NAME = ${COMMUNITY_NAME}, but it should have been - COMMUNITY_NAME=${COMMUNITY_NAME}, without the spaces.

          Now if I do docker-compose up -d it takes the .env variables, but I can also do docker-compose --env-file .env.development up -d to specify an alternative env file. Which actually isn't what I expected (I expected --env-file to be compulsory), but honestly as far as I'm concerned it's even better this way.