Moby: Best way to set the timezone

Created on 4 Apr 2015  Â·  24Comments  Â·  Source: moby/moby

Hello.

I would very much like to do this:

FROM dreamcat4/tvheadend

# Set the time zone
RUN echo "Europe/London" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata
VOLUME /etc/timezone /etc/localtime

But unfortunately docker isn't having any of it.

 ---> Running in d46c13cdee09
INFO[0004] setup mount namespace mounting /var/lib/docker/vfs/dir/a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447 into /var/lib/docker/aufs/mnt/d46c13cdee097af451025ff1c0c88a7be5903861e7ee5d68c16b91dbe33c0f92/etc/localtime not a directory 
ERROR: exit status 1

/var/lib/docker/vfs/dir# ls -lsa a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447/
total 20
 4 drwxr-xr-x 2 root root  4096 Apr  4 20:21 .
16 drwx------ 4 root root 16384 Apr  4 20:21 ..
# 

Most helpful comment

use TZ environment variable. In vanilla unix, the libraries will observe this, the presence/content of /etc/timezone is irrelevant.

[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=UTC centos /bin/bash
[root@007860c5f2f7 /]# date
Fri Nov 27 17:06:27 UTC 2015
[root@007860c5f2f7 /]# exit
exit
[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=EST centos /bin/bash
[root@2854e80c4e6f /]# date
Fri Nov 27 12:06:37 EST 2015
[root@2854e80c4e6f /]#

[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=Europe/London centos /bin/bash
da[root@3a778f0585cc /]# date
Fri Nov 27 17:10:18 GMT 2015
[root@3a778f0585cc /]#

All 24 comments

You need to do that when you run the container with -v

On Saturday, April 4, 2015, Dreamcat4 [email protected] wrote:

Hello.

I would very much like to do this:

FROM dreamcat4/tvheadendMAINTAINER dreamcat4 <[email protected]

Set the time zoneRUN echo "Europe/London" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdataVOLUME /etc/timezone /etc/localtime

But unfortunately docker isn't having any of it.

---> Running in d46c13cdee09
INFO[0004] setup mount namespace mounting /var/lib/docker/vfs/dir/a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447 into /var/lib/docker/aufs/mnt/d46c13cdee097af451025ff1c0c88a7be5903861e7ee5d68c16b91dbe33c0f92/etc/localtime not a directory
ERROR: exit status 1

/var/lib/docker/vfs/dir# ls -lsa a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447/
total 20
4 drwxr-xr-x 2 root root 4096 Apr 4 20:21 .
16 drwx------ 4 root root 16384 Apr 4 20:21 ..#

—
Reply to this email directly or view it on GitHub
https://github.com/docker/docker/issues/12084.

If you need help with using docker please see #docker on freenode or
forums.docker.com this issue tracker is for bugs with docker

On Saturday, April 4, 2015, Jessica Frazelle [email protected] wrote:

You need to do that when you run the container with -v

On Saturday, April 4, 2015, Dreamcat4 <[email protected]

Hello.

I would very much like to do this:

FROM dreamcat4/tvheadendMAINTAINER dreamcat4 [email protected]

Set the time zoneRUN echo "Europe/London" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdataVOLUME /etc/timezone /etc/localtime

But unfortunately docker isn't having any of it.

---> Running in d46c13cdee09
INFO[0004] setup mount namespace mounting /var/lib/docker/vfs/dir/a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447 into /var/lib/docker/aufs/mnt/d46c13cdee097af451025ff1c0c88a7be5903861e7ee5d68c16b91dbe33c0f92/etc/localtime not a directory
ERROR: exit status 1

/var/lib/docker/vfs/dir# ls -lsa a9b69bda5e03469519f0a32ca5d79e2b01af0242cfa64d37c3afd44ce2067447/
total 20
4 drwxr-xr-x 2 root root 4096 Apr 4 20:21 .
16 drwx------ 4 root root 16384 Apr 4 20:21 ..#

—
Reply to this email directly or view it on GitHub
https://github.com/docker/docker/issues/12084.

@jfrazelle So are you saying that _regular docker volumes work for single files from the cmd line, but not in a Dockerfile?_. If that is true then am pretty surprised by that. I was under the impression that only worked for bind mounted volumes (you didn't say).

But regardless if that were true, I believe such way would not be as suitable / convenient for my specific intended usage. As it would be inconsistent when setting up all of the other config data for my app from within that particular Dockerfile.

I now think (sorry, after filing my original report) that there may be another way to circumvent / work around the limitation. All from within the Dockerfile(s). Just by making a new folder and then symlinking the 2 individual files on to their proper location in the underlying container. Then mounting a volume over that folder.

Example:

base Dockerfile:

FROM ubuntu-debootstrap:14.04
RUN mkdir /tz && mv /etc/timezone /tz/ && mv /etc/localtime /tz/ \
 && ln -s /tz/timezone /etc/ && ln -s /tz/localtime /etc/

localized dockerfile:

FROM base

# Set the time zone
RUN echo "Europe/London" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata \
 && cp /etc/timezone /tz/ && cp /etc/localtime /tz/
VOLUME /tz

In other words, in the base image those files are missing or set to the distro's generic default. However the symlinks are created to /tz folder. Which then can be mounted like any regular volume, containing the sparse file(s).

[EDIT]
Although I have yet to test this ^^ until tomorrow. We don't want dog-reconfigure to destroy those symlinks we created for this purpose in the base image.

It is useful for the time zone data, as that is an often-localized part of the image. When generating variants for different countries / region. Which is a common thing for many apps (including whenever setting up PHP etc).

Right. Well that work-around did not keep things all so simple in the end. Perhaps at run-time may be overall a more elegant way to set the time zone. Or like this:

https://github.com/sameersbn/docker-gitlab/issues/77#issuecomment-46346176

Ok. I have determined that actually there is no foolproof elegant way to set the time zone inside of a docker container. So have finally settled on this solution:

App dockerfile:

# Relocate the timezone file
RUN mkdir -p /config/etc && mv /etc/timezone /config/etc/ && ln -s /config/etc/timezone /etc/

App entrypoint script:

# Set timezone as specified in /config/etc/timezone
dpkg-reconfigure -f noninteractive tzdata

Data volume /config dockerfile, localized to a specific country or region:

# Set the time zone
RUN echo "Europe/London" > /config/etc/timezone

... it is not elegant b/c involving 3 seperate files, and re-creating /etc/localtime on every runtime container start. Which is rather wasteful. However it does work properly, and successfully achieve separation between the base app image, and each per-country localized configuration. In 3 lines of code.

I mount /etc/localtime in my images so it is in sync w my host -v
/etc/localtime:/etc/localtime

On Saturday, April 4, 2015, Dreamcat4 [email protected] wrote:

Ok. I have determined that actually there is no foolproof elegant way to
set the time zone inside of a docker container. So have finally settled on
this solution:

App dockerfile:

Relocate the timezone fileRUN mkdir -p /config/etc && mv /etc/timezone /config/etc/ && ln -s /config/etc/timezone /etc/

App entrypoint script

Set timezone as specified in /config/etc/timezone

dpkg-reconfigure -f noninteractive tzdata

Data volume /config dockerfile, localized to a specific country or region:

Set the time zoneRUN echo "Europe/London" > /config/etc/timezone

... it is not elegant b/c involving 3 seperate files, and re-creating
/etc/localtime on every runtime container start. Which is rather
wasteful. However it does work properly, and successfully achieve
separation between the base app image, and each per-country localized
configuration. In 3 lines of code.

—
Reply to this email directly or view it on GitHub
https://github.com/docker/docker/issues/12084#issuecomment-89690544.

@jfrazelle Yes that is the most popular way. And I also had been doing that myself until now. However it is not correct b/c does not work when the software requires instead the file /etc/timezone to be set. That way you are using ^^ leaves it as the default value Etc/UTC.

You can just declare another volume for the other file. But kinnda messy. And you have to remember to keep including those 2 volumes for every docker run command.

My situation:

Also needs other localization changes not just only the time zone. e.g. to set the language, set the currency, date format and so on. Then it makes sense to do all of those things in one place. For that

So I have separate Dockerfile(s) to seed the app's initial configration data. Where all of the relevant localisation is set. One for me and other users can easily make for themselves. This is the Data Containers paradigm. When we create the data container with certain initial data - that is also an opportunity to set these things. And remains seperate from the App Image. Then we run our app with --volumes-from <data container>.

@jfrazelle
Doesn't the fact that there are so many hacks and workarounds presented to answer many, many requests for how to set the container time zone mean that this should be considered basic functionality which is not working and therefore a bug?

Hello again. i've just uploaded a real-world example of my solution.

  • In base image do this:

https://github.com/dreamcat4/docker-images/blob/master/tvh/master/Dockerfile#L36-45

  • and in the runtime start script do this:

https://github.com/dreamcat4/docker-images/blob/master/tvh/master/entrypoint.sh#L6-7

  • Build a regional 'config' image (volumes-from import the /config folder as a user-mounted volume)

https://github.com/dreamcat4/docker-images/blob/master/tvh/config/Dockerfile#L8-20

And that workaround (for me) solves my timezone issue. At least, so long as i've got other kinds things to put in the config volume. Then it's no extra hassle. I can appreciate that just purely for timezone only, that would be rather an overkill.

How come this is still not marked as feature/bug whatever? i believe its pretty critical for production systems... and everyone is rolling its own workaround/hack to make it work based on
Host OS / Docker Base OS .. thats crazy and basically breaks the portability...

use TZ environment variable. In vanilla unix, the libraries will observe this, the presence/content of /etc/timezone is irrelevant.

[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=UTC centos /bin/bash
[root@007860c5f2f7 /]# date
Fri Nov 27 17:06:27 UTC 2015
[root@007860c5f2f7 /]# exit
exit
[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=EST centos /bin/bash
[root@2854e80c4e6f /]# date
Fri Nov 27 12:06:37 EST 2015
[root@2854e80c4e6f /]#

[jfraney@openldap-testvm ~]$ sudo docker run -i -t -e TZ=Europe/London centos /bin/bash
da[root@3a778f0585cc /]# date
Fri Nov 27 17:10:18 GMT 2015
[root@3a778f0585cc /]#

@jjfraney Thanks for that. It works great on bare metal. However it doesn't seem to work on boot2docker. It looks like b2d assumes the host time is UTC :-(

@darkmatter. Some docker containers won't have access to tz data, and so TZ will have no effect. The b2d MAY still be setting the TZ environment correctly, or as you say, it may not. This is not really about what b2d 'assumes'....its only about making sure the TZ environment variable is conveyed to the process running in the container. If b2d can export any variable, it can export TZ.

Try this to isolate, though. This busybox container does not handle TZ....there is no tzdata installed, so it doesn't know how to localize the time. The result of date command does not show what is intended (time localized to Chicago), but the TZ variable is set correctly so b2d did its job. (I used b2d-1.9.0 via docker toolbox.)

docker@default:~$ docker run -it -e TZ=America/Chicago busybox /bin/sh
/ # date
Fri Nov 27 19:59:24 UTC 2015
/ # echo $TZ
America/Chicago

@jjfraney I completely agree. Your method works perfectly on Ubuntu (and all LSB distros?) and b2d is passing in the TZ variable. I just want to figure out why it's assuming the time on my Mac is UTC. The first date command is run on my Mac. Note the Madrid time.

~ > date
Sat 28 Nov 2015 09:51:39 CET
 ~ > docker run -t -i --rm -e TZ=Europe/Madrid ubuntu date
Sat Nov 28 10:51:50 CET 2015
 ~ > docker run -t -i --rm -e TZ=Europe/London ubuntu date
Sat Nov 28 09:51:57 GMT 2015
 ~ > docker run -t -i --rm -e TZ=America/Chicago ubuntu date
Sat Nov 28 03:52:25 CST 2015
 ~ > docker run -t -i --rm -e TZ=Europe/Madrid busybox date
Sat Nov 28 09:52:56 UTC 2015
 ~ > docker run -t -i --rm -e TZ=Europe/London busybox date
Sat Nov 28 09:53:07 UTC 2015
 ~ > docker run -t -i --rm -e TZ=America/Chicago busybox date
Sat Nov 28 09:53:18 UTC 2015
 ~ >

fwiw- wanted to share the amalgamated approach we've taken to handle this:
https://github.com/sameersbn/docker-gitlab/issues/77#issuecomment-159774461

The best solution i consider and use is to define an environment variable in your dockerfile as bellow:
ENV TZ 'America/Sao_Paulo'

Or run docker specifying the timezone like this:
$ docker run -it -e TZ='America/Sao_Paulo' ubuntu

For me it works in Dockerfile
ENV TZ=Europe/London RUN echo $TZ | tee /etc/timezone RUN dpkg-reconfigure --frontend noninteractive tzdata

ps. if You are NOT ROOT you have to add 'sudo' before 2nd (before 'tee') and 3rd line at the beginning.

Haven't they changed things in ubuntu 16.04? Not sure this works for that distro anymore.

@dreamcat4 it works on for mysql image version 5.6

FROM mysql:5.6
ENV TZ=Europe/London
RUN echo $TZ | tee /etc/timezone
RUN dpkg-reconfigure --frontend noninteractive tzdata
...

it works for me :+1:
there are now the first lines of all my dockerfiles.
FROM georchestra/ldap
RUN echo Europe/Paris | tee /etc/timezone
RUN dpkg-reconfigure --frontend noninteractive tzdata

@dreamcat4 found this http://stackoverflow.com/a/40235306. Seems to work for me with ubuntu 16.04 guest...

a day late but how bout just

ENV TZ="/usr/share/zoneinfo/America/Los_Angeles"

that seemed to work great for me

Comment above seems to work corectly but not for all images. Ubuntu 16.04+ based, for example, is affected by a tzdata package bug as reported here.

Latest versions of Ubuntu image doesn't contain the tzdata package by default:
https://github.com/docker-library/official-images/pull/2852#issuecomment-293697246

So you should install tzdata in Dockerfile first.

If the TZ variable is set, installing a package that depends on tzdata (eg. php) will auto configure it upon installation. Therefore, just setting the TZ env variable seems to work. The /etc/timezone and /etc/localtime is still using UTC but date and other commands like php, python or mysql seems to use the timezone set in TZ env variable.

If you really want to have the correct values in /etc/timezone and /etc/localtime, then following contents in Dockerfile seems to do the trick:

FROM ubuntu:latest

ENV TZ Asia/Kuala_Lumpur

RUN echo $TZ > /etc/timezone && \
    apt-get update && apt-get install -y tzdata && \
    rm /etc/localtime && \
    ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata && \
    apt-get clean

I've put this here as this is the first result that appears when searching for how to set tz in docker.

Was this page helpful?
0 / 5 - 0 ratings