Skip to content

IA-4811 Create optimized Dockerfile for production#2832

Open
oschvr wants to merge 23 commits intodevelopfrom
IA-4811-prepare-optimized-dockerfile-nginx
Open

IA-4811 Create optimized Dockerfile for production#2832
oschvr wants to merge 23 commits intodevelopfrom
IA-4811-prepare-optimized-dockerfile-nginx

Conversation

@oschvr
Copy link
Copy Markdown

@oschvr oschvr commented Mar 18, 2026

What problem is this PR solving?

Bundling all build steps for frontend and backend in one single, multistage, non-root Dockerfile meant to run for prod

Related JIRA tickets

IA-4811

Changes

  • Bundled frontend and backend into a multistage Dockerfile
  • Optimised the backend stage with a virtual environment (venv)
  • Carry forward venv to a slim python 3.9 image so it is more lightweight

How to test

run docker build -t iaso:test . and then docker image ls , you should see the docker container built and the size (content size)

Once you build it you can follow these instructions to spin up locally:

In the new docker-compose.yml file (meant for prod use), you have to make sure to populate the env vars, either through a .env or in your environment itself.

You will need a way to spin up a postgres:postgis instance and pass the connection details as env vars too.

Print screen / video

Screenshot 2026-03-18 at 15 32 33 Screenshot 2026-03-18 at 14 57 39

Notes

Things that the reviewers should know:

The npmbuilder step RUN npm run webpack-prod is very memory intensive, so if you're planning to build locally, make sure of the following:

  • Have Docker desktop installed
  • Go to Dashboard > Settings > Resources
  • Increase Memory Limit to >= 12Gb

Doc

Docker Compose

You will need to populate at least these environment variables with your own values in a .env file:

# PostgreSQL Database connection details
RDS_DB_NAME=
RDS_HOSTNAME=
RDS_PASSWORD=
RDS_USERNAME=
DB_READONLY_PASSWORD=
DB_READONLY_USERNAME=

# Used for encryption and authorisation
ENCRYPTED_TEXT_FIELD_KEY=
SECRET_KEY=

# To interact with Enketo/ODK
ENKETO_API_TOKEN=
ENKETO_SIGNING_SECRET=

# Docker image tag (defaults to "latest" if not set)
IMAGE_TAG=

Docker Compose automatically reads a .env file in the project root, so no source command is needed.

Alternatively, you can uncomment the env_file: section in docker-compose.prod.yml to pass the variables directly to the container.

Note: for production deployments you need an external PostgreSQL database. The db service in the compose file is only intended for local development. Make sure your RDS_* variables point to your production database.

Proceed to run docker compose on your server:

docker compose -f docker-compose.prod.yml up

This will pull the necessary containers (iaso & nginx) and spin up the service at port 80.

@oschvr oschvr changed the title feat: Create docker-compose for production (iaso+nginx @ AWS EB) IA-4811 Create optimized Dockerfile for production Mar 18, 2026
@oschvr oschvr requested a review from tdethier March 19, 2026 11:06
@oschvr oschvr marked this pull request as ready for review March 19, 2026 11:06
Copy link
Copy Markdown
Member

@tdethier tdethier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After struggling a bit, I managed to run this prod image locally 👍

I ran the setuper on this server, which worked fine

Image

options:
max-size: '5k'
command: start_dev
image: blsq/iaso:${IMAGE_TAG:-latest}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should revert all the changes in this file, so that it can still be used for local development

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget about this @oschvr

@@ -1,4 +0,0 @@
option_settings:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we keep this PR open, we can leave these files deleted. But once we merge, we need to make sure that all environments have switched to docker (or at least staging ones) because removing these EBS files will break deployments

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oschvr @tdethier I would have tried the approach of the console deploying a subdirectory for docker eb and leaving the "normal" env untouched (so you can keep beanstalk for a moment for other iaso variants)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's kinda what @oschvr has done so far, but not exactly

@oschvr
Copy link
Copy Markdown
Author

oschvr commented Mar 26, 2026

If you're migrating to Docker, and you get this error:

Invalid option specification (Namespace: 'aws:elasticbeanstalk:environment:proxy:staticfiles', OptionName: '/static'): Unknown configuration setting.

This one means that the django.config from .ebextensions already created the static files config, so we need to remove it from the environment. Run this command to remove it:

aws elasticbeanstalk update-environment --environment-name "${EB_ENVIRONMENT}" --options-to-remove Namespace=aws:elasticbeanstalk:environment:proxy:staticfiles,OptionName=/static

ENKETO_DEV: ${ENKETO_DEV:-false}
ENKETO_SIGNING_SECRET: ${ENKETO_SIGNING_SECRET}
ENKETO_URL: ${ENKETO_URL}
FAVICON_PATH: ${FAVICON_PATH}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you check env vars on the server, you see that they are all blank

Image

This is probably the reason why we don't have a favicon or an app title and so on:

  • the env var value is not set in terraform (because we're fine with the default from django settings)
  • docker compose turns this non-set var into a blank string, because we're explicitly passing it here
  • django settings notice that there's a env var and use it instead of using the default value

We've had the same issue on console. We fixed it by calling a custom method in settings that filters out blank strings. There might be a better way though. Let's discuss this together when you're available!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants