Can't start docker container, acquis.yaml is a directory

I’m following this guide to set up CrowdSec for the first time for NPM. I did the initial launch, generated the CROWDSEC_BOUNCER_APIKEY, added it to .env, and restarted the container, but it keep restarting itself with the following in the log:

crowdsec  | time="2024-06-21T20:22:03Z" level=info msg="Loaded 48 scenarios"
crowdsec  | time="2024-06-21T20:22:03Z" level=info msg="loading acquisition file : /etc/crowdsec/acquis.yaml"
crowdsec  | time="2024-06-21T20:22:03Z" level=fatal msg="crowdsec init: while loading acquisition config: failed to yaml decode /etc/crowdsec/acquis.yaml: yaml: input error: read /etc/crowdsec/acquis.yaml: is a directory"

acquis.yaml was indeed a directory that it created, so I tried deleting that and creating it as a file myself. When I try to launch the compose file after that, I get this from Docker:

Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/home/user/yami/npm/crowdsec/acquis.yaml" to rootfs at "/etc/crowdsec/acquis.yaml": mount /home/user/yami/npm/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml (via /proc/self/fd/6), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type

So Docker doesn’t like when it’s a file, and CrowdSec doesn’t like when it’s a directory. What should I do from here?

For reference, this is my docker-compose.yml:

name: nginx-proxy-manager
services:
  npm:
    image: 'lepresidente/nginxproxymanager:latest'
    container_name: npm
    hostname: npm
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
    environment:
      DB_MYSQL_HOST: "npm"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_PASSWORD: ${DATABASE_PASSWORD}
      DB_MYSQL_NAME: "npm"
      CROWDSEC_OPENRESTY_BOUNCER: |
        ENABLED=true
        API_URL=http://crowdsec:8080
        API_KEY=${CROWDSEC_BOUNCER_APIKEY}
    volumes:
      - /home/user/yami/npm/data:/data
      - /home/user/yami/npm/letsencrypt:/etc/letsencrypt
    depends_on:
      db:
        condition: service_healthy
    security_opt:
      - no-new-privileges=true
    networks:
      - crowdsec-net
      - npm-net

  db:
    image: 'mariadb:lts'
    restart: unless-stopped
    networks:
      npm-net:
    environment:
      MYSQL_ROOT_PASSWORD: ${ROOT_DATABASE_PASSWORD}
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm'
      MYSQL_PASSWORD: "${DATABASE_PASSWORD}"
    volumes:
      - /home/user/yami/npm/npm-db:/var/lib/mysql
    security_opt:
      - no-new-privileges=true
    healthcheck:
      test: ['CMD', '/usr/local/bin/healthcheck.sh', '--innodb_initialized']
      start_period: 5s
      timeout: 5s
      interval: 5s
      retries: 5

  crowdsec:
    image: docker.io/crowdsecurity/crowdsec:latest
    container_name: crowdsec
    environment:
      - COLLECTIONS=crowdsecurity/nginx-proxy-manager
    volumes:
      - /home/user/yami/npm/crowdsec/db:/var/lib/crowdsec/data/
      - /home/user/yami/npm/crowdsec/config:/etc/crowdsec/
      - /home/user/yami/npm/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
      - /home/user/yami/npm/data/logs/:/var/log/npm:ro
    networks:
      crowdsec-net:
    restart: unless-stopped
    security_opt:
      - no-new-privileges=true

networks:
  crowdsec-net:
    driver: bridge
    name: crowdsec-net
  npm-net:
    driver: bridge
    name: npm-net

So docker is trying to mount /{local_path}/ as the value itself since there is no $ infront of the { so it doesn’t know its an environment variable you want to use. By default docker creates directories when the file does not exist on the host itself so as long as you define local_path in your .env and update the compose to use ${local_path} and the file exists it should work as expected. However, if local_path is the full path then you can drop the pre pending / within the definitions.

Sorry I wasn’t clear, me using /{local_path} was just for convenience here, not literally what is in the compose file. Treat where you see that as if it were /home/user/yami/npm. I’ll edit the post to remove this confusing thought I had.

Ahh my bad, I presumed incorrectly. Is docker running as root?

I have the same issue as this and I have been stuck for weeks. This is only a problem with Crowdsec image, other images are able to correctly create a yaml file through docker compose volume.

I’ll also note that if I try to manually add acquis.yaml in the host, it’ll give another error:

Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/home/opc/docker/crowdsec/acquis.yaml" to rootfs at "/etc/crowdsec/acquis.yaml": mount /home/opc/docker/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml (via /proc/self/fd/6), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type

Yeah I just had the same error via docker on my production system (another application but its the same error), it seems file mappings seems to be very fragile. The only way I managed to get it running is mapping to the parent folder instead of the file itself.

This used to work so I dont know if there any open issues on docker compose.

I created a docker issue for it, hopefully it’ll be resolved.

Hmm that is what docker does by default, it creates a directory if the file doesnt exist so its not a bug. The bug in question is if the file exists on host and tries mapping it, the daemon responds with an error for some reason.

The reason docker creates a directory as it doesnt know what you want since in unix you can have a directory named acquis.yaml so it just presumes that what you want.