Last Updated: December 30, 2020
·
51.2K
· murphyrandle

Things I learned while writing a Dockerfile

I've been writing a Dockerfile for work that installs Postgresql and sets up a database. I ran into a few gotchas along the way. Here's the documentation of my misconceptions and the solutions.

One Line VS Multiple Lines

I originally was using one RUN command per shell line that I wanted to exectue:

Example:

RUN service postgresql start 
RUN su postgres -c "createuser -d -r -s root" 

The Problem

I wanted to separate the commands in order to optimize recreation of other images. But I'd always get the error that postgres wasn't running. That's exactly because each RUN command is a separate layer of running machine. Starting postgres in a RUN command does nothing because as soon as the next RUN command is reached, I'm in a new VM, and there's no postgres running at all.

The Solution

Join commands that are part of one cohesive set up step into one RUN statement:
(Note, thanks @outrunthewolf for pointing out that line continuations \ can be used to carry the same command across multiple lines for readability).

RUN service postgresql start && \
    su postgres -c "createuser -d -r -s root"

sudo VS su

The Problem

Online guides for installing and setting up postgres instruct the user to run commands as sudo, but this won't work with a Dockerfile because the commands are running as root, and root doesn't have access to the sudo command.

The Solution

Use su {USER} -c "commands go here" instead to execute commands as a different user.

An example Dockerfile

Here's an example Dockerfile that installs postgresql, creates a root role, and creates a database called accounts.

FROM ubuntu
MAINTAINER Murphy Randle, murphy@spacemonkey.com

## Install Postgresql deps
RUN apt-get install -y postgresql postgresql-contrib

## Postgresql setup:
RUN service postgresql start && su postgres -c "createuser -d -r -s root" && createdb accounts && service postgresql stop

ENTRYPOINT ["/bin/bash"]

7 Responses
Add your response

You should maintain multiple lines for readability by adding && \ and dropping your command to the next line.

over 1 year ago ·

That's a good point, @outrunthewolf! Thanks!

over 1 year ago ·

Thanks for the tip on running su from inside a dockerfile. It was exactly what I needed.

over 1 year ago ·

Currently docker allows for even cleaner approch. You can just swap to specific user for next run commands. See:

RUN id
USER app
ENV HOME /app
RUN id

PS: I tend to add ENV HOME as some scripts (i.e. bower) want's to edit files in ${HOME}/.config.bla

over 1 year ago ·

hmm... unknown text processor. Let's try again :)

RUN id

USER app

ENV HOME /app

RUN id

over 1 year ago ·

@munhitsu
The USER command is useful when all RUN statements can be executed as that user. But what about when specific RUN commands need to be done under specific users? Can the USER command be used more than once in a Dockerfile?

Eg:

USER root

RUN echo 'export ORACLE_SID=XE' >> /etc/bash.bashrc

USER oracle

RUN sqplus @my_schema.sql

over 1 year ago ·

Yes, that's exactly how it works. You can use USER as many times as you want.

over 1 year ago ·