Mojolicious and Docker
This post plans to illustrate how you can build and dockerize and Mojolicious application and solve basic issue like volume mounts permission and env values. Building an simple dockerized app Prerequisites: curl -L http://cpanmin.us | perl - App::cpanminus cpanm --verbose Carton At this point we can create an cpanfile and Mojolicious and some other dependencies in it. requires 'Mojolicious' => '9.39', Generate the app: carton install carton exec -- mojo generate app Generate the Dockerfile: touch Dockerfile And populated with the following lines of code: FROM perl:latest LABEL version="1.0" LABEL description="Foo Bar foo.bar@gmail.com" # add C dependencies RUN apt-get update && apt-get install -y \ build-essential \ libssl-dev \ libxml2-dev \ libexpat1-dev \ zlib1g-dev \ libpq-dev \ libmariadb-dev \ mariadb-client \ libdbd-mariadb-perl \ ssh \ && rm -rf /var/lib/apt/lists/* # Clean up to reduce image size # install Carton RUN curl -L http://cpanmin.us | perl - App::cpanminus && \ cpanm --notest --verbose Carton # create a new user and group for your app RUN groupadd -r my_app_group && useradd -r -g my_app_group -m -d /home/my_app my_app_user # create home dir for that user RUN mkdir -p /home/my_app && \ chown -R my_app_user:my_app_group /home/my_app # switch from root to the new user USER my_app_user WORKDIR /home/my_app ENV PATH="/home/my_app/.perl/bin:${PATH}" # Copy application dependencies and files COPY --chown=my_app_user:my_app_group cpanfile /home/my_app COPY --chown=my_app_user:my_app_group cpanfile.snapshot /home/my_app # Install dependencies using Carton RUN carton install --deployment EXPOSE 3000 ENTRYPOINT ["carton", "exec", "--", "morbo", "my_app/script/my_app"] Add the docker compose file: # touch docker compose services: web: image: my_app ports: - "3000:3000" build: context: . dockerfile: Dockerfile working_dir: /home/my_app # Set the working directory env_file: - .env volumes: - ./my_app:/home/my_app/my_app:ro - ./.env:/home/my_app/.env:ro Now run: docker compose build docker compose up The landing page for Mojolicious should be available at http://127.0.0.1:3000 What just happened: We locally ran carton install and and created an local Mojo app. Running locally carton install creates the cpanfile.snapshot that it used by the Docker image to handle the Perl module versioning. The Docker file create an base image from perl:latest, installed essential OS dependencies, used Carton to handle Perl module dependencies, created a non-root user skin_care_user with proper permissions and setup morbo server via ENTRYPOINT directive. Then compose.yaml file mounts the application and the .env file and maps the port "3000:3000"

This post plans to illustrate how you can build and dockerize and Mojolicious application and solve basic issue like volume mounts permission and env values.
Building an simple dockerized app
- Prerequisites:
curl -L http://cpanmin.us | perl - App::cpanminus
cpanm --verbose Carton
At this point we can create an cpanfile and Mojolicious and some other dependencies in it.
requires 'Mojolicious' => '9.39',
- Generate the app:
carton install
carton exec -- mojo generate app
- Generate the Dockerfile:
touch Dockerfile
And populated with the following lines of code:
FROM perl:latest
LABEL version="1.0"
LABEL description="Foo Bar foo.bar@gmail.com"
# add C dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libssl-dev \
libxml2-dev \
libexpat1-dev \
zlib1g-dev \
libpq-dev \
libmariadb-dev \
mariadb-client \
libdbd-mariadb-perl \
ssh \
&& rm -rf /var/lib/apt/lists/* # Clean up to reduce image size
# install Carton
RUN curl -L http://cpanmin.us | perl - App::cpanminus && \
cpanm --notest --verbose Carton
# create a new user and group for your app
RUN groupadd -r my_app_group && useradd -r -g my_app_group -m -d /home/my_app my_app_user
# create home dir for that user
RUN mkdir -p /home/my_app && \
chown -R my_app_user:my_app_group /home/my_app
# switch from root to the new user
USER my_app_user
WORKDIR /home/my_app
ENV PATH="/home/my_app/.perl/bin:${PATH}"
# Copy application dependencies and files
COPY --chown=my_app_user:my_app_group cpanfile /home/my_app
COPY --chown=my_app_user:my_app_group cpanfile.snapshot /home/my_app
# Install dependencies using Carton
RUN carton install --deployment
EXPOSE 3000
ENTRYPOINT ["carton", "exec", "--", "morbo", "my_app/script/my_app"]
- Add the docker compose file:
# touch docker compose
services:
web:
image: my_app
ports:
- "3000:3000"
build:
context: .
dockerfile: Dockerfile
working_dir: /home/my_app # Set the working directory
env_file:
- .env
volumes:
- ./my_app:/home/my_app/my_app:ro
- ./.env:/home/my_app/.env:ro
Now run:
docker compose build
docker compose up
The landing page for Mojolicious should be available at
http://127.0.0.1:3000
What just happened:
We locally ran carton install
and and created an local Mojo app. Running locally carton install
creates the cpanfile.snapshot
that it used by the Docker image to handle the Perl
module versioning.
The Docker file create an base image from perl:latest
, installed essential OS dependencies, used Carton
to handle Perl
module dependencies, created a non-root user skin_care_user
with proper permissions and setup morbo
server via ENTRYPOINT
directive. Then compose.yaml
file mounts the application and the .env
file and maps the port "3000:3000"