Timezones are a common source of confusion when containerizing an application. Will your cron tasks run at the right time? Docker containers don’t inherit the host’s timezone, so you can run into unexpected scheduling issues that wreak havoc with your application.
Here’s the date
command running natively on an Ubuntu 20.04 host in the British Summer Time timezone:
And here’s the same command in a container based on an unmodified ubuntu:20.04
image:
The container is using the UTC timezone, creating a one hour difference between the two times.
How Do Linux Timezones Work?
Most Linux distributions use the tzdata
package to provide timezone information. When tzdata
is installed, you can inspect the current timezone by reading the /etc/timezone
file:
You’ll also have an /etc/localtime
file. This is a symlink to the correct timezone database for the selected location:
If you change the timezone, using dpkg-reconfigure tzdata
, the /etc/localtime
symlink gets updated to point to the new database. The output of commands like date
will be adjusted to include the active timezone’s offset.
The Problem With Containers
The challenge with containers derives from setting the timezone in the first place. If you think back to when you setup your host, you’d have needed to set the timezone as part of the operating system installation. When you run a new container, it starts immediately though, without any “installation” stage. There’s no opportunity to select an appropriate timezone.
Theoretically, container runtimes could offer automatic inheritance of the host’s timezone. This doesn’t happen as it might lead to unexpected results when deploying to remote environments. It would be easy to overlook that your cron schedule works on your local machine but executes unexpectedly in a managed Kubernetes cluster using UTC time.
Container images ship with a baked-in timezone instead. For most popular images, this will be UTC. Many base images, particularly minimal ones, won’t even include the tzdata
package. You’ll have no /etc/timezone
or /etc/localtime
files.
Adding Timezones to Your Containers
The first part of setting the proper timezone is to make sure tzdata
is installed. If your image doesn’t include it, you’ll need to manually add the package as part of your Dockerfile
.
FROM ubuntu:latest ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y tzdata
When tzdata
installs, you usually get an interactive prompt that lets you select the correct timezone from a menu. This is unhelpful when you’re programmatically building Docker containers. Setting the DEBIAN_FRONTEND
environment variable suppresses the prompt and defaults the timezone to UTC.
Once you’ve got tzdata
into your image, you’re ready to configure the correct timezone for your application. The simplest approach is to set the TZ
environment variable to the timezone you want to use:
FROM ubuntu:latest ENV TZ=Europe/London ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y tzdata
If you prefer, you can set the TZ
variable when you start containers. Pass it as an environment variable to docker run
. This lets you override an image’s default timezone, provided it includes the tzdata
package.
docker run -e TZ=Europe/London -it ubuntu:latest
An alternative to environment variables is the /etc/timezone
file. You can write the required timezone as part of your Dockerfile
. If you use this method, you must reconfigure tzdata
using your package manager. Remember to use non-interactive mode or you’ll receive the graphical timezone prompt again.
FROM ubuntu:latest RUN echo "Europe/London" > /etc/timezone RUN dpkg-reconfigure -f noninteractive tzdata
Other Techniques
If you want to guarantee timezone synchronization with the host, you can mount your local tzdata
files into your containers. You’ll still need tzdata
inside the container for this to work correctly.
docker run -v /etc/timezone:/etc/timezone -v /etc/localtime:/etc/localtime -it ubuntu:latest
Although Docker doesn’t provide any built-in support for timezones, that’s not true of all container engines. Podman has a dedicated --tz
flag which lets you set the timezone when creating a new container:
podman run --tz=Europe/London -it ubuntu:latest
Behind the scenes, Podman will mount an appropriate /etc/localtime
file for you. The specified timezone will persist for the lifetime of the container.
Podman also lets you set a default timezone for containers created without the --tz
flag. Create or edit .config/containers/containers.conf
in your home directory. Add a tz
setting on a new line in the file:
# Used when no --tz flag is given tz = "Europe/London"
Podman’s native timezone integration makes it easier to work with than Docker. As Podman’s CLI is compatible with Docker’s, making the switch can be worth considering if you’re frequently working with containers in different timezones.
Summary
Timezones are often overlooked when setting up Docker containers. Most base images default to UTC time which can lead to confusion when the host’s timezone is different.
By installing the tzdata
package, your container gains compatibility with all timezones via the TZ
environment variable, /etc/timezone
, and /etc/localtime
. Alternatively, you can sync your host’s timezone by mounting the relevant files into your containers.
Finally, remember that these considerations also apply to hosted Docker services and Kubernetes clusters. Your containers will use UTC time unless instructed otherwise. As long as you can set environment variables, you’ll be able to use TZ
to adjust the timezone for your workloads.