A common pattern is to render config files at container startup from env vars and a template.
Dockerfile:
COPY nginx.conf.j2 /etc/nginx/nginx.conf.j2
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]docker-entrypoint.sh:
#!/usr/bin/env sh
set -e
jinja2 /etc/nginx/nginx.conf.j2 - --format env <<EOF >/etc/nginx/nginx.conf
SERVER_NAME=$SERVER_NAME
UPSTREAM=$UPSTREAM
EOF
exec nginx -g "daemon off;"$ jinja2 template.j2 data.json --format json$ jinja2 template.j2 data.yaml --format yaml$ cat data.json | jinja2 template.j2 - --format jsonMerge multiple data files together. Later files override values from earlier files using deep merge:
$ jinja2 template.j2 base.json overrides.yaml production.jsonExample with nested structure:
base.json:
{
"app": "myapp",
"server": {
"host": "localhost",
"port": 3000
},
"debug": false
}production.yaml:
server:
port: 8080
debug: falseResult after merge:
{
"app": "myapp",
"server": {
"host": "localhost",
"port": 8080
},
"debug": false
}Note that server.host is preserved from base.json while server.port is overridden by production.yaml.
$ jinja2 template.j2 data.json --format json -D foo=bar -D answer=42Use dot notation to set nested dictionary values:
$ jinja2 template.j2 data.json -D server.host=localhost -D server.port=8080This is equivalent to:
{
"server": {
"host": "localhost",
"port": "8080"
}
}Dot notation merges with existing data, so you can override specific nested values without replacing the entire structure.
Template:
PATH is {{ environ('PATH') }}
USER is {{ environ('USER') }}
USERNAME is {{ environ('USERNAME') }}
Run:
$ jinja2 template.j2 - --format env <<'EOF'
PATH=$PATH
USER=$USER
USERNAME=$USERNAME
EOF$ jinja2 template.j2 data.json -e do -e loopcontrols
$ jinja2 template.j2 data.json -e myext:MyExtensionSee extensions.md for details.
# Import a custom filter module
$ jinja2 template.j2 data.json -F myfilters
# Import specific filter function
$ jinja2 template.j2 data.json -F myfilters.reverse
# Use Ansible filters
$ jinja2 template.j2 data.json -F ansible.plugins.filter.coreSee filters.md for detailed examples and patterns.
Use -I to add directories to the template search path. This is useful when
templates need to include or import from a shared directory:
project/
├── templates/
│ ├── macros/
│ │ └── buttons.j2
│ └── pages/
│ └── home.j2
└── data.json
home.j2:
{% from "macros/buttons.j2" import button %}
{{ button("Click me") }}$ jinja2 templates/pages/home.j2 data.json -I templates/
Use -S to read the template from stdin, useful for one-liners:
$ echo '{{ 1 + 1 }}' | jinja2 -S
2
$ echo 'Hello {{ name }}!' | jinja2 -S -D name=world
Hello world!
$ echo 'Home: {{ environ("HOME") }}' | jinja2 -S
Home: /home/user
Stream mode also supports data files:
$ echo 'Hello {{ name }}!' | jinja2 -S data.json
Hello World!
$ echo '{{ greeting }} {{ name }}!' | jinja2 -S base.json overrides.yaml
Hello World!
Freedom of the Press Foundation uses jinja2-cli to generate Dockerfiles for Dangerzone, a tool for converting potentially dangerous PDFs into safe ones.
See: Makefile
poetry run jinja2 Dockerfile.in Dockerfile.env > DockerfileElastic uses jinja2-cli to generate Dockerfiles and docker-compose configs for their official Beats, Logstash, and Kibana Docker images.
jinja2 -D beat=$@ -D elastic_version=$(ELASTIC_VERSION) \
templates/Dockerfile.j2 > build/$@/Dockerfile-fullScyllaDB uses jinja2-cli in their machine image tooling for generating cloud deployment configurations.
Have a public example? Add a link here.