Skip to content

Instantly share code, notes, and snippets.

@zmajstor zmajstor/
Last active Apr 4, 2019

What would you like to do?
Docker on CentOS Linux

NGINX reverse-proxy with Let's Encrypt Certs with Docker Compose

Acts as a single reverse proxy with SSL offloading for any number of dockerized web projects on a single server, using 2 awesome projects:


  • the reverse proxy is be the only container that actually needs to expose any ports to the world.
  • base image is just a regular nginx
  • the only container that actually exposes anything to the outside world, both port 80 and 443
  • any other Docker Web Container or Docker-Compose stack with a specific environment variables, that lands in the same bridge network, will automatically be reverse-proxied and provided with a SSL certificate from LetsEncrypt.
  • has read-only access to the docker host's docker.sock
    • this is how the docker host will notify this container about other containers starting or stopping
  • will watch for any new container with an environment variable called "VIRTUAL_HOST"
    • if any such starting container is found, it will update it's nginx.tmpl file, create a new server directive for this new container and push the changes to the proxy container
    • similarly, if any such container is stopped, it will remove the corresponding entry from the server directives and push the updated config to the proxy container
  • gains read-only access to ./data/proxy/certs


  • will handle obtaining and renewing certificates from letsEncrypt
  • also has read-only access to the docker host's docker.sock
    • just like the docker-gen container, this container will be notified about any starting or stopping containers
  • will watch for any new container with environment properties called "LETSENCRYPT_HOST" and "LETSENCRYPT_EMAIL"
    • will check if there are certificate files for the domain name set in the "LETSENCRYPT_HOST" property of any such started container
      • if none are found, it will try to obtain new certificate files for this domain, also using the "LETSENCRYPT_EMAIL" address
      • if certificate files for this domain already exists, it will try to renew any certificate that would expire within 30 days every 3600 seconds

Swarm job scheduler (cron implementation through go routines)

Cron entries are stored in an array, sorted by their next activation time. Cron sleeps until the next job is due to be run. Upon waking:

  • it runs each entry that is active on that second
  • it calculates the next run times for the jobs that were run
  • it re-sorts the array of entries by next activation time.
  • it goes to sleep until the soonest job.

Entry                  | Description                                | Equivalent To
-----                  | -----------                                | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 *
@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly                | Run once a week, midnight between Sat/Sun  | 0 0 0 * * 0
@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * *
@hourly                | Run once an hour, beginning of hour        | 0 0 * * * *
@every <duration>      | Schedule after <duration></duration>

For example, @every 1h30m10s would indicate a schedule that activates after 1 hour, 30 minutes, 10 seconds, and then every interval after that. Note: The interval does not take the job runtime into account. For example, if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, it will have only 2 minutes of idle time between each run.

set -e
# source:
echo "cleanup old stuff ..."
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo
echo "installing docker-ce & cli"
sudo yum install -y docker-ce docker-ce-cli
sudo systemctl start docker
echo "docker install finished"
docker --version
## installing docker-compose
#### check for the latest release on
sudo curl -L "$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
set -e
yum install -y yum-utils device-mapper-persistent-data lvm2
yum install -y docker-ce
sudo systemctl enable docker.service
sudo systemctl daemon-reload && sudo systemctl restart docker.service

Configure dockerd to start on boot (systemd)

Enable docker

sudo systemctl enable docker

Edit docker service

sudo systemctl edit docker.service

... and enter:

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://

... and after that restart service:

sudo systemctl daemon-reload && sudo systemctl restart docker.service

Add docker-compose service

sudo vi /etc/systemd/system/docker-compose-app.service
Description=Docker infra

ExecStart=/usr/local/bin/docker-compose -f /root/docker-compose.yml up -d
ExecStop=/usr/local/bin/docker-compose -f /root/docker-compose.yml down

systemctl enable docker-compose-app
systemctl daemon-reload && systemctl restart docker.service

Useful commands to check running service

sudo netstat -lntp | grep dockerd
sudo netstat -tuplen
# docker network create --driver overlay --attachable webproxy_network
# docker-compose up -d
version: '2'
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: always
- "80:80"
- "443:443"
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- dhparam:/etc/nginx/dhparam
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- webproxy_network
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: nginx-proxy-le
restart: always
- nginx-proxy
- certs:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
- webproxy_network
image: portainer/portainer
container_name: portainer-web
command: -H unix:///var/run/docker.sock
restart: always
- 9000:9000
- ""
- ""
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
- webproxy_network
- portainer_agent_network
external: true
external: true
set -e
## Building Docker Image on Local Workstation
docker login -u $REPO_USER
docker build -t $APPNAME . && \
docker tag $APPNAME:latest $REPOIMAGE:latest && \
docker push $REPOIMAGE:latest
docker service create \
--name cron_scheduler \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
-e "TZ=Europe/Paris" \
-e "LOG_LEVEL=debug" \
-e "LOG_JSON=false" \
--constraint "node.role == manager" \
FROM ruby:2.5.3-alpine
RUN apk --no-cache --update add git nodejs postgresql-client postgresql-dev tzdata yarn
# && npm install -g -s --no-progress yarn
# python3 && pip3 --no-cache-dir install awscli s3cmd
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
# Different layer for gems installation
COPY Gemfile Gemfile.lock package.json ./
RUN apk --no-cache add --virtual build-deps build-base ruby-dev && \
bundle install --without development test --deployment --no-cache --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 && \
apk del build-deps
# Copy the application into the container
COPY . .
# precompile assets with dummy SECRET_KEY_BASE
RUN SECRET_KEY_BASE=1 RAILS_ENV=production DISABLE_SPRING=1 bin/rails assets:precompile
# cleanup
RUN find / -type f -iname \*.apk-new -delete && \
rm -rf /var/cache/apk/* && \
rm -rf /usr/lib/lib/ruby/gems/*/cache/* && \
rm -rf ~/.gem && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/* && \
yarn cache clean
# RUN chown -R nobody:nogroup /app
# USER nobody
# ENV RACK_ENV=production \
# RAILS_ENV=production \
# PORT=3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.