title: Gitlab CI Split Docker Build Into Multiple Stages
body: <p>I have a react/django app that's dockerized.  There's 2 stages to the GitLab CI process.  Build_Node and Build_Image.  Build node just builds the react app and stores it as an artifact.  Build image runs <code>docker build</code> to build the actual docker image, and relies on the node step because it copies the built files into the image.</p>
  <p>However, the build process on the image takes a long time if package dependencies have changed (apt or pip), because it has to reinstall everything.</p>
  <p>Is there a way to split the docker build job into multiple parts, so that I can say install the apt and pip packages in the dockerfile while build_node is still running, then finish the docker build once that stage is done?</p>
  <h2>gitlab-ci.yml:</h2>
  <pre><code>stages:
    - Build Node Frontend
    - Build Docker Image
  
  services:
    - docker:18.03-dind
  
  variables:
    DOCKER_DRIVER: overlay2
    DOCKER_HOST: tcp://localhost:2375
    DOCKER_TLS_CERTDIR: &quot;&quot;
  
  build_node:
    stage: Build Node Frontend
    only:
      - staging
      - production
    image: node:14.8.0
    variables:
      GIT_SUBMODULE_STRATEGY: recursive
    artifacts:
      paths:
        - http
    cache:
      key: &quot;node_modules&quot;
      paths:
        - frontend/node_modules
    script:
      - cd frontend
      - yarn install --network-timeout 100000
      - CI=false yarn build
      - mv build ../http
  
  
  
  build_image:
    stage: Build Docker Image
    only:
      - staging
      - production
    image: docker
  
    script:
      #- sleep 10000
      - tar -cvf app.tar api/ discordbot/ helpers/ http/
      - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
      #- docker pull $CI_REGISTRY_IMAGE:latest
      #- docker build --network=host --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA --tag $CI_REGISTRY_IMAGE:latest .
      - docker build --network=host --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA --tag $CI_REGISTRY_IMAGE:latest .
      - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
      - docker push $CI_REGISTRY_IMAGE:latest
  </code></pre>
  <h2>Dockerfile:</h2>
  <pre><code>FROM python:3.7-slim
  
  # Add user
  ARG APP_USER=abc
  RUN groupadd -r ${APP_USER} &amp;&amp; useradd --no-log-init -r -g ${APP_USER} ${APP_USER}
  
  WORKDIR /app
  ENV PYTHONUNBUFFERED=1
  EXPOSE 80
  EXPOSE 8080
  
  ADD requirements.txt /app/
  
  RUN set -ex \
      &amp;&amp; BUILD_DEPS=&quot; \
          gcc \
      &quot; \
      &amp;&amp; RUN_DEPS=&quot; \
          ffmpeg \
          postgresql-client \
          nginx \
          dumb-init \
      &quot; \
      &amp;&amp; apt-get update &amp;&amp; apt-get install -y $BUILD_DEPS \
      &amp;&amp; pip install --no-cache-dir --default-timeout=100000 -r /app/requirements.txt \
      &amp;&amp; apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false  $BUILD_DEPS \
      &amp;&amp; apt-get update &amp;&amp; apt-get install -y --no-install-recommends $RUN_DEPS \
      &amp;&amp; rm -rf /var/lib/apt/lists/*
  
  # Set uWSGI settings
  ENV UWSGI_WSGI_FILE=/app/api/api/wsgi.py UWSGI_HTTP=:8000 UWSGI_MASTER=1 UWSGI_HTTP_AUTO_CHUNKED=1 UWSGI_HTTP_KEEPALIVE=1 UWSGI_LAZY_APPS=1 UWSGI_WSGI_ENV_BEHAVIOR=holy PYTHONUNBUFFERED=1 UWSGI_WORKERS=2 UWSGI_THREADS=4
  ENV UWSGI_STATIC_EXPIRES_URI=&quot;/static/.*\.[a-f0-9]{12,}\.(css|js|png|jpg|jpeg|gif|ico|woff|ttf|otf|svg|scss|map|txt) 315360000&quot;
  ENV PYTHONPATH=$PYTHONPATH:/app/api:/app
  ENV DB_PORT=5432 DB_NAME=shittywizard DB_USER=shittywizard DB_HOST=localhost
  
  ADD nginx.conf /etc/nginx/nginx.conf
  
  # Set entrypoint
  ADD entrypoint.sh /
  RUN chmod 755 /entrypoint.sh
  ENTRYPOINT [&quot;dumb-init&quot;, &quot;--&quot;, &quot;/entrypoint.sh&quot;]
  
  ADD app.tar /app/
  
  RUN python /app/api/manage.py collectstatic --noinput
  </code></pre>