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"

Apr 27, 2025 - 19:14
 0
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"