First Dockerfile, I would appreciate critique 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 &amp;&amp; \
    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 ] &amp;&amp; [ ! -z "$TZ" ]; then
  cp /usr/share/zoneinfo/$TZ /etc/localtime
  echo $TZ &gt;/etc/timezone
fi

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

# Create password file
echo "[$RELAY_IP]:$RELAY_PORT   $EMAIL:$PASS" &gt; /etc/postfix/sasl_passwd &amp;&amp; \
chown root:root /etc/postfix/sasl_passwd &amp;&amp; \
chmod 600 /etc/postfix/sasl_passwd &amp;&amp; \
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
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 &amp;&amp; \
    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).