I have server in Ubuntu. On the server I have docker compose
installed (v2.29.7). I created the script ssl-renew.sh
to periodically renew certbot certs via crontab. Certbot is run through the docker the same way as my nginx, they are in the same docker-compose.yml
I DON’T have "docker-compose" because it gives me this error: DockerException: Error while fetching server API version: HTTPConnection.request()
, and the fix is clearly stated here – just move to newest ‘docker compose’ without the dash
The problem is that "docker compose" version just does not work when I try to capture the output.
The failing part of the script:
#!/bin/bash
set -x
DOCKER_COMPOSE_FILE="/services/nginx/docker-compose.yml"
echo "starting renewal"
output=$(/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE run --rm certbot renew --force-renewal 2>&1)
echo "docker script has run"
The output is always the same:
+ DOCKER_COMPOSE_FILE=/services/nginx/docker-compose.yml
+ echo 'starting renewal'
starting renewal
++ /usr/bin/docker compose -f /services/nginx/docker-compose.yml run --rm certbot renew --force-renewal
It never reaches echo "docker script has run"
. The command is not interactive, when I remove the line in script to this one:
#output=$(/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE run --rm certbot renew --force-renewal 2>&1)
/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE run --rm certbot renew --force-renewal
It is working:
+ DOCKER_COMPOSE_FILE=/services/nginx/docker-compose.yml
+ echo 'starting renewal'
starting renewal
+ /usr/bin/docker compose -f /services/nginx/docker-compose.yml run --rm certbot renew --force-renewal
WARN[0000] /services/nginx/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Renewing an existing certificate for example.com
Failed to renew certificate example.com with error: urn:ietf:params:acme:error:rateLimited :: There were too many requests of a given type :: Error creating new order :: too many certificates (5) already issued for this exact set of domains in the last 168 hours: example.com, retry after 2024-10-02T07:04:55Z: see https://letsencrypt.org/docs/duplicate-certificate-limit/
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
All renewals failed. The following certificates could not be renewed:
/etc/letsencrypt/live/example.com/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 renew failure(s), 0 parse failure(s)
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.
+ echo 'docker script has run'
Now it run the command and terminal didn’t freeze, but I want to capture the output and send it via API to logging provider. I tried everything in Google and GPT at the moment, nothing helped. Please, if you have any advices – I would appreciate.
2
Answers
The problem was simple I was missing
-it
argument:Not working one:
output=$(/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE run --rm certbot renew --force-renewal 2>&1)
Working one:
output=$(/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE run -it --rm certbot renew --force-renewal 2>&1)
there are few things to note.
you are using command substitution as follows
in your case, you are assigning the output of the command within
$()
to a variable nameoutput
. if you will print the value of theoutput
variable (e.g.echo "$output"
), you would see the output of the internal$()
command, e.g.docker compose
.from the output your shared, it does not seem to be "working" since it still does not print
which means that
docker compose
does not terminate and the script never reaches theecho "docker script has run"
line.