I want to make a docker container of my Flask App. I am running an open-source hydrological model called 'Wflow' (see github) inside my flask app. The Wflow runs with Julia command.
julia_process = subprocess.Popen("julia", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Julia commands to be executed
julia_commands = f"""
using Wflow
Wflow.run("{toml_path}")
"""
# Send the commands to Julia through the process's standard input
output, errors = julia_process.communicate(julia_commands)
# syntax=docker/dockerfile:1
# Use Mambaforge as the base image for Conda support
FROM condaforge/mambaforge:latest
# Set maintainer info
LABEL maintainer="Maarten Pronk <maarten.pronk@deltares.nl>"
# Copy .cdsapirc to root directory for configuration
COPY .cdsapirc /root/.cdsapirc
# Create application directory
RUN mkdir -p /app
WORKDIR /app
# Set the default shell for proper Conda usage
SHELL ["/bin/bash", "-c"]
# Install system dependencies
RUN apt-get update && apt-get install -y \
g++ git gcc gunicorn curl libcurl4-openssl-dev \
&& rm -rf /var/lib/apt/lists/*
# Create HydroMT-WFlow environment with Julia support
RUN mamba create -n hydromt-wflow -c conda-forge \
hydromt_wflow python=3.11 julia gunicorn -y
# Set Conda's default environment for all future commands
ENV CONDA_DEFAULT_ENV=hydromt-wflow
ENV PATH=/opt/conda/envs/hydromt-wflow/bin:$PATH
ENV JULIA_DEPOT_PATH="/opt/julia"
# Ensure pip is installed inside the Conda environment
RUN conda run -n hydromt-wflow python -m ensurepip && \
conda run -n hydromt-wflow python -m pip install --upgrade pip
# Copy requirements.txt and install dependencies inside Conda environment
COPY requirements.txt /app/requirements.txt
RUN test -f /app/requirements.txt && conda run -n hydromt-wflow pip install --no-cache-dir -r requirements.txt || echo "No requirements.txt found"
RUN julia -e 'println("Starting Wflow installation..."); \
using Pkg; \
Pkg.update(); \
println("Registry updated"); \
Pkg.add(name="Wflow"); \
println("Wflow installed successfully"); \
using Wflow; \
println("Wflow loaded and verified")'
# Copy source code into container
COPY . .
# Expose the application port
EXPOSE 5000
ENTRYPOINT ["conda", "run", "--no-capture-output", "-n", "hydromt-wflow"]
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "5", "--timeout", "5000", "--log-level", "debug", "--access-logfile", "-", "--error-logfile", "-", "app:app"]
I also tried this inside docker to run Julia:
RUN julia -e 'using Pkg; Pkg.develop(PackageSpec(url="https://github.com/Deltares/Wflow.jl.git"))'
RUN julia -e 'using Pkg; \
Pkg.develop(PackageSpec(url="https://github.com/Deltares/Wflow.jl.git")); \
Pkg.instantiate(); \
using Wflow; \
println("Wflow successfully loaded")'
Any help would be greatly appreciated, been stuck on this for quite a while.
Create and save content of the Dockerfile.
mkdir -p julia-wflow
cd julia-wflow
nano Dockerfile
and enter and save this content in the Dockerfile:
# Force Ubuntu x86-64 (amd64) even on Apple Silicon
# The docker build and run commands contain enforcing information
# of amd64 architecture
# in ubuntu - you don't need to do anything specially
# pull ubuntu image
FROM ubuntu:latest
# Set environment variables to avoid interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Install dependencies
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
tar \
unzip \
&& rm -rf /var/lib/apt/lists/*
# Install Julia for x86-64 (amd64)
RUN curl -fsSL https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.1-linux-x86_64.tar.gz | tar -xz -C /opt/
# Set environment path for Julia
ENV PATH="/opt/julia-1.10.1/bin:$PATH"
# Verify Julia installation
RUN julia --version
# Copy the Julia script
COPY install_wflow.jl /install_wflow.jl
# Run the script
RUN julia /install_wflow.jl
# Default to Julia interactive shell
CMD ["julia"]
If you are on a MacOS, then ensure availability of emulator
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
on MacOS you have to manually stop and restart Docker Desktop first. I did this with MacOS which has a arm64 architecture - which complicated everything.
Check in MacOS whether QEMU setup properly
docker run --rm --platform linux/amd64 ubuntu uname -m
if it prints x86_64, the setup worked. if it prints aarch64, Docker would be running in arm64 mode.
We build in MacOS by enforcing amd architecture. I guess in linux this works, too.
docker buildx build --platform linux/amd64 --progress=plain -t julia-wflow .
after successful build, check:
docker inspect julia-wflow | grep Architecture
## should give: "Architecture": "amd64"
finally, run the container:
docker run --rm --platform linux/amd64 -it julia-wflow
which throws you into a running Julia session!
I have once wrote articles about this topic how to run amd64 Docker container or also a virtual machine inside arm64 MacOS (M1, M2, M3), if you want to read in more detail:
If you want to run Python in addition, let's create a new environment for this:
mkdir -p julia-wflow-app
cd julia-wflow-app
Create the following files:
.
├── Dockerfile
├── app.py
├── julia_bridge.jl
└── requirements.txt
I will give the files with their inputs:
nano Dockerfile
# Use Mambaforge base image (with conda and Python)
FROM condaforge/mambaforge:latest
# Metadata
LABEL maintainer="Your Name <you@example.com>"
# Set working directory
WORKDIR /app
# Set environment to avoid interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Install system dependencies
RUN apt-get update && apt-get install -y \
g++ git gcc curl tar unzip libcurl4-openssl-dev \
&& rm -rf /var/lib/apt/lists/*
# Manually install Julia (x86-64)
RUN curl -fsSL https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.1-linux-x86_64.tar.gz | tar -xz -C /opt/
ENV PATH="/opt/julia-1.10.1/bin:$PATH"
ENV JULIA_DEPOT_PATH="/opt/julia"
# Install Julia package Wflow
RUN julia -e 'println("Starting Wflow installation..."); \
using Pkg; Pkg.update(); println("Registry updated"); \
Pkg.add(name="Wflow"); println("Wflow installed successfully"); \
using Wflow; println("Wflow loaded and verified")'
# Create and activate a new conda environment
RUN mamba create -n hydromt-wflow -c conda-forge \
python=3.11 gunicorn flask -y
# Activate the environment by updating PATH
ENV CONDA_DEFAULT_ENV=hydromt-wflow
ENV PATH=/opt/conda/envs/hydromt-wflow/bin:$PATH
# Install Python dependencies with pip (optional)
COPY requirements.txt .
RUN test -f requirements.txt && \
conda run -n hydromt-wflow pip install --no-cache-dir -r requirements.txt || \
echo "No requirements.txt found"
# Copy app and Julia bridge script
COPY app.py julia_bridge.jl ./
# Expose port for web app
EXPOSE 5010
# Entrypoint: run Flask app via gunicorn using Conda environment
ENTRYPOINT ["conda", "run", "--no-capture-output", "-n", "hydromt-wflow"]
CMD ["gunicorn", "--bind", "0.0.0.0:5010", "--workers", "4", "app:app"]
nano app.py
from flask import Flask, jsonify
import subprocess
app = Flask(__name__)
@app.route("/")
def home():
return "Hello from Python + Julia!"
@app.route("/run-julia")
def run_julia():
try:
output = subprocess.check_output(["julia", "julia_bridge.jl"], universal_newlines=True)
return jsonify({"output": output})
except subprocess.CalledProcessError as e:
return jsonify({"error": e.output}), 500
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=5010)
nano julia_bridge.jl
using Wflow
println("Wflow is available: ", isdefined(Main, :Wflow))
println("Random example number from Julia: ", rand(1:100))
nano requirements.txt
flask
requests
julia
After creating and saving all files, build and run using these commands:
docker buildx build --platform linux/amd64 -t julia-wflow-app .
docker run --platform linux/amd64 --rm -it -p 5010:5010 julia-wflow-app
Output of the latter will be sth like:
[2025-03-21 12:33:40 +0000] [15] [INFO] Starting gunicorn 23.0.0
[2025-03-21 12:33:40 +0000] [15] [INFO] Listening at: http://0.0.0.0:5010 (15)
[2025-03-21 12:33:40 +0000] [15] [INFO] Using worker: sync
[2025-03-21 12:33:40 +0000] [16] [INFO] Booting worker with pid: 16
[2025-03-21 12:33:40 +0000] [17] [INFO] Booting worker with pid: 17
[2025-03-21 12:33:40 +0000] [18] [INFO] Booting worker with pid: 18
[2025-03-21 12:33:40 +0000] [19] [INFO] Booting worker with pid: 19
and it will wait there.
Go to your OS's favorite browser and enter as URL:
http://localhost:5010
It should show:
Hello from Python + Julia!
Now enter:
http://localhost:5010/run-julia
And you will see:
{"output":"Wflow is available: true\nRandom example number from Julia: 22\n"}
Voila! You have both running!