2
\$\begingroup\$

As the title says, this is my first Dockerfile. It won't be published on any registry, just for my internal use at home. I'd appreciate any critique of what I could do better. I'm currently working on a way to not hard-code the password into the docker-compose file...

This image is to run Postfix on Alpine as a SMTP relay for my devices to alert me (e.g., NAS, security cameras, etc...).

Dockerfile

FROM alpine:3.10

ARG BUILD_DATE

LABEL maintainer="MyNameGoesHere" \
      org.label-schema.schema-version="1.0" \
      org.label-schema.name="postfixrelay" \
      org.label-schema.build-date=$BUILD_DATE

RUN apk add --no-cache --update \
    bash ca-certificates cyrus-sasl-login postfix rsyslog tzdata && \
    rm -rf /var/cache/apk/*

EXPOSE 25

VOLUME [ "/var/spool/postfix" ]

COPY ./entrypoint.sh /

RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

#!/bin/sh -e

#Set timezone
if [ ! -f /etc/timezone ] && [ ! -z "$TZ" ]; then
  cp /usr/share/zoneinfo/$TZ /etc/localtime
  echo $TZ >/etc/timezone
fi

# Set Postfix options
postconf -e "inet_interfaces = all" && \
postconf -e "mynetworks = 0.0.0.0/0" && \
postconf -e "relayhost = [$RELAY_IP]:$RELAY_PORT" && \
postconf -e "smtp_use_tls = yes" && \
postconf -e "smtp_sasl_auth_enable = yes" && \
postconf -e "smtp_sasl_security_options = noanonymous" && \
postconf -e "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"&& \
postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt" && \

# Create password file
echo "[$RELAY_IP]:$RELAY_PORT   $EMAIL:$PASS" > /etc/postfix/sasl_passwd && \
chown root:root /etc/postfix/sasl_passwd && \
chmod 600 /etc/postfix/sasl_passwd && \
postmap /etc/postfix/sasl_passwd

# Start postfix
postfix start-fg

docker-compose.yml

version: '3'
services:
  postfixrelay:
    container_name: postfixrelay
    restart: unless-stopped
    environment:
      - TZ=America/New_York
      - RELAY_IP=smtp.gmail.com
      - RELAY_PORT=587
      - EMAIL=my_email_address@gmail.com
      - PASS=password
    networks:
      - postfixrelay
    ports:
      - '25:25'
    volumes:
      - 'postfixrelay_data:/var/spool/postfix'
    image: postfixrelay

networks:
  postfixrelay:

volumes:
  postfixrelay_data:
    driver: local
\$\endgroup\$
  • 2
    \$\begingroup\$ shameless self-promotion 😉 refer to stackoverflow.com/questions/58393555/… \$\endgroup\$ – Efrat Levitan Oct 24 '19 at 18:04
  • \$\begingroup\$ You can split the apk add ... packages on multiple lines in your Dockerfile, makes it easier to see what's changed in i.e. a git diff like this. I'd move the VOLUME and EXPOSE to before the RUN as they'll probably never change. For the networks you can use the default setting the name: postfixrelay in your docker-compose.yml. \$\endgroup\$ – masseyb Oct 24 '19 at 18:12
1
\$\begingroup\$

I would set the execute permissions on the entrypoint.sh before building the docker image - RUN, COPY and ADD create layers - COPY preserves the file permissions so you can remove the RUN chmod +x /entrypoint.sh (a layer from your final image).

FROM alpine:3.10

ARG BUILD_DATE

LABEL maintainer="MyNameGoesHere" \
      org.label-schema.schema-version="1.0" \
      org.label-schema.name="postfixrelay" \
      org.label-schema.build-date=$BUILD_DATE

EXPOSE 25

VOLUME [ "/var/spool/postfix" ]

COPY ./entrypoint.sh /

RUN apk add --no-cache --update \
        bash \
        ca-certificates \
        cyrus-sasl-login \
        postfix \
        rsyslog \
        tzdata && \
    rm -rf /var/cache/apk/*

ENTRYPOINT ["/entrypoint.sh"]

... moved the EXPOSE, VOLUME and COPY instructions above the RUN - the VOLUME and EXPOSE are not likely to change and the build cache for the COPY will only be invalidated if the contents of the file changes as explained in the ADD or COPY best practices. Finally, split the packages being added on multiple lines to make it clearer at a glance in i.e. a git diff when something changes.

Concerning the entrypoint.sh I'd suggest taking a look at the Exec form ENTRYPOINT example to ensure that your process is receiving the Unix signals.

For the docker-compose I'd pass the PASS environment variable from the hosts environment and name the default network explicitly:

version: '3'
services:
  postfixrelay:
    container_name: postfixrelay
    restart: unless-stopped
    environment:
      ...
      - PASS=${PASS}
...
networks:
  default:
    name: postfixrelay
...

If you want to docker-compose up --scale postfixrelay=3 you'd need to stop using container_name, trying to start multiple containers with the same name will conflict. You'd also need to stop publishing the ports to the host, will have port conflicts - would need to set up a tcp load balancer (i.e. nginx, traefik, ...) and proxy the connections to your upstream services.

Then there is docker swarm and kubernetes (or other) as proper orchestration managers for your service(s).

\$\endgroup\$
0
\$\begingroup\$

In entrypoint.sh, I prefer to have set -eu in line 2 instead of passing it in line 1. That way it is guaranteed to be set no matter how the program is run.

Because of this set -e, you can omit the && \, since that code is neither in a subshell nor in a function. (The set -e mode is unreliable in either of them, I don't remember the exact rules though.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.