Report this

What is the reason for this report?

Nginx docker container exits with error “fopen:No such file or directory:fopen('/etc/nginx/ssl/live/test.example.dev/fullchain.pem'” for SSL

Posted on February 1, 2022

Okay so I’m learning Docker and I am trying to deploy a test app with a subdomain (who’s domain was bought from another provider) which is pointing to my server. The server already has non-dockerized Nginx setup which serves couple of other non-dockerized apps perfectly. And that part means Nginx is already using port 80 and 443. And the subdomain somehow shows Nginx default page when visited. This is my server situation. Now let me explain what happens with the dockerized app.

The app is using 4 images to create 4 containers: Nodejs, Mongodb, Nginx and Certbot. Before adding Certbot, I could perfectly access the app with <server-ip>:<port>. But now I need to attach that subdomain (test.example.dev) to my app with Let’s Encrypt SSL certificates.

So after the build is done with Docker Compose, I see that Nginx and Certbot is exited with errors.

This is my nginx/default.conf file:

server {
    listen 80;
    listen [::]:80;

    server_name test.example.dev;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://test.example.dev$request_uri;
    }
}

server {
    listen 443 default_server ssl http2;
    listen [::]:443 ssl http2;

    server_name test.example.dev;

    ssl_certificate /etc/nginx/ssl/live/test.example.dev/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/test.example.dev/privkey.pem;
    
    location /api {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://practice-app:3050;
        proxy_redirect off;

    }
}

Here’s my shared docker-compose file for both dev and prod environment:

version: '3'
services:

  practice-app:
    build: .
    depends_on:
      - mongo
      
  nginx: 
    image: nginx:stable-alpine
    ports: 
      - "4088:80"
    volumes: 
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - practice-app

  mongo:
    image: mongo:4.4.6
    environment:
      - MONGO_INITDB_ROOT_USERNAME=test
      - MONGO_INITDB_ROOT_PASSWORD=test
    volumes:
      - mongo-db:/data/db
      
volumes:
  mongo-db:

And here’s my docker-compose.prod.yml file:

version: "3"
services: 

  practice-app:
    build: 
      context: .
      args: 
        NODE_ENV: production
    environment:
      - NODE_ENV=production
    command: node index.js

  nginx: 
    ports: 
      - "4088:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certbot/www:/var/www/certbot/:ro
      - ./certbot/conf/:/etc/nginx/ssl/:ro

  certbot:
    image: certbot/certbot:latest
    volumes:
      - ./certbot/www/:/var/www/certbot/:rw
      - ./certbot/conf/:/etc/letsencrypt/:rw
    depends_on:
      - nginx
      
  mongo: 
    environment:
      - MONGO_INITDB_ROOT_USERNAME=test
      - MONGO_INITDB_ROOT_PASSWORD=test 

Nginx logs says:

/docker-entrypoint.sh: Configuration complete; ready for start up
2022/01/31 13:42:28 [emerg] 1#1: cannot load certificate "/etc/nginx/ssl/live/test.example.dev/fullchain.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/nginx/ssl/live/test.example.dev/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
nginx: [emerg] cannot load certificate "/etc/nginx/ssl/live/test.example.dev/fullchain.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/nginx/ssl/live/test.example.dev/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)

And Certbot logs says:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Certbot doesn't know how to automatically configure the web server on this system. However, it can still get a certificate for you. Please run "certbot certonly" to do so. You'll need to manually configure your web server to use the resulting certificate.

But after adding the following code:

command: certonly --webroot -w /var/www/certbot --force-renewal --email example@gmail.com -d test.example.dev --agree-tos

under certbot service, the log changed to this:

[17:00] [server1.com test] # docker logs test_certbot_1

Saving debug log to /var/log/letsencrypt/letsencrypt.log Skipped user interaction because Certbot doesn't appear to be running in a terminal. You should probably include --non-interactive or --force-interactive on the command line. 

Account registered. 
Requesting a certificate for test.example.dev

Certbot failed to authenticate some domains (authenticator: webroot). The Certificate Authority reported these problems:

Domain: test.example.dev 
Type:   unauthorized 
Detail: Invalid response from http://test.example.dev/.well-known/acme-challenge/J7_MZAg5yYLi3P_VVEalOwlU5cH3NAmevlhTKCvt6Zo [xx.xx0.xxx.7x]: "<html>\r\n<head><title>404 Not Found</title></head>\r\n<body>\r\n<center><h1>404 Not Found</h1></center>\r\n<hr><center>nginx</center>\r\n"

Hint: The Certificate Authority failed to download the temporary challenge files created by Certbot. Ensure that the listed domains serve their content from the provided --webroot-path/-w and that files created there can be downloaded from the internet.

Some challenges have failed. Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.

What am I doing wrong here? Please give me a beginner friendly solution as I am new to DevOps.



This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

Hello,

The issue you’re facing involves multiple aspects, including Nginx configuration, Docker Compose setup, and Certbot SSL certificate generation. Let’s address these step by step to get your setup working.

Understanding the Problem

  1. Nginx and Certbot Containers Exiting: Your Nginx container tries to start before the SSL certificates are available, causing it to exit because it can’t find the specified certificate files. Similarly, Certbot is having trouble validating your domain because it cannot place the challenge files in a location where they can be served by Nginx due to incorrect or insufficient configuration.

  2. Port Conflicts: You mentioned that the server already has a non-Dockerized Nginx instance running on ports 80 and 443. This setup will conflict with any Docker containers trying to bind to the same ports. Since you’re mapping port 4088 to 80 for Nginx inside Docker, ensure external requests to your domain on port 80/443 are correctly routed to port 4088 of your Dockerized Nginx.

  3. Domain Validation Failed: Certbot is unable to validate your domain because the ACME challenge requests from Let’s Encrypt are not reaching the /.well-known/acme-challenge/ location served by your Dockerized Nginx.

Solutions

Step 1: Ensure Correct Port Forwarding

Since your server’s primary Nginx instance is already using ports 80 and 443, you need to ensure it forwards ACME challenge requests to your Dockerized Nginx:

  • Configure a location block in your server’s primary Nginx for the ACME challenge that proxies requests to your Dockerized Nginx (port 4088).
location /.well-known/acme-challenge/ {
    proxy_pass http://localhost:4088;
}

Step 2: Fix Docker Compose and Nginx Configuration

  • Update your Docker Compose to use a common network for Nginx and Certbot, ensuring they can communicate properly. Define a custom network at the bottom of your docker-compose.prod.yml and assign it to both services.
networks:
  default:
    name: my_network

Step 3: Certbot Configuration

  • Modify the Certbot command to ensure it waits for Nginx to be fully up and serving the challenge directory before attempting to obtain certificates.
command: >
  sh -c "sleep 10 &&
         certonly --webroot -w /var/www/certbot --force-renewal --email example@gmail.com -d test.example.dev --agree-tos --non-interactive"

The sleep 10 command is a simple way to wait for Nginx to be up. Adjust the sleep duration as necessary.

Step 4: Solve Nginx Container Exiting

  • Initially, use self-signed certificates or placeholders for SSL certificate and key in your Nginx configuration to prevent the container from exiting. You can replace these with real certificates obtained by Certbot after the first successful run.
ssl_certificate /etc/nginx/ssl/dummy.crt;
ssl_certificate_key /etc/nginx/ssl/dummy.key;

Generate dummy certificates or place any temporary certificate files at the paths specified in your Nginx configuration, and update the paths to the real certificates once Certbot has successfully obtained them.

Step 5: Volume Permissions

  • Ensure the Docker volumes for Certbot (./certbot/www/ and ./certbot/conf/) are writable by the Certbot container and readable by the Nginx container. This ensures Certbot can write the challenge files and certificates where Nginx expects them.

After addressing these points, redeploy your Docker Compose setup. Ensure that the ACME challenge requests can reach your Dockerized Nginx, and that Certbot can successfully validate your domain and obtain certificates.

Best,

Bobby

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Start building today

From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.

Dark mode is coming soon.