Skip to content

Deploy from Docker Hub

Lunni is easy to use if you have even just a little experience with Docker and Docker Compose. You can easily use the same docker-compose.yml for development on your local machine and deployment with Lunni.

To try it out, let's deploy Gitea! Gitea provides the following docker-compose.yml, which is an excellent starting point:

version: "3"

networks:
  gitea: { external: false }

services:
  server:
    image: gitea/gitea:1.17.3
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
    networks: [gitea]
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    depends_on: [db]

  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks: [gitea]
    volumes:
      - ./postgres:/var/lib/postgresql/data

Step 1. Use named volumes

Open up New project page on Lunni, pick a name and paste the Docker Compose file. We'll need to make a few tweaks, but that will only take a minute.

First of all, when deploying with Lunni, you can't mount paths from current directory, as the actual docker-compose.yml file is not stored anywhere in the filesystem. Let's use named volumes instead:

version: "3"

volumes:
  gitea-data:
  postgres-data:
...
services:
  server:
    ...
    volumes:
      - ./giteagitea-data:/data
... same for postgres

Step 2. Define public network and add labels to the HTTP service

Lunni uses Traefik for proxying HTTP. This allows you to run multiple HTTP services on one server, but you'll need to let Traefik know about your service somehow. To do that, add an external network named traefik-public:

version: "3"

...
networks:
  gitea: { external: false }
  traefik-public: { external: true }

services:
  server:
    ...
    networks: [gitea, traefik-public]
...

Remove the HTTP port (which will go through Traefik instead):

services:
  server:
    ...
    ports:
      - "3000:3000"
      - "222:22"
...

Then add the labels for Traefik to pick up:

services:
  server:
    ...
    deploy:
      labels:
        - traefik.enable=true
        - traefik.docker.network=traefik-public
        - traefik.constraint-label=traefik-public
        - traefik.http.routers.${PROJECT_NAME?}-http.rule=Host(`${DOMAIN?}`)
        - traefik.http.routers.${PROJECT_NAME?}-http.entrypoints=http
        - traefik.http.routers.${PROJECT_NAME?}-http.middlewares=https-redirect
        - traefik.http.routers.${PROJECT_NAME?}-https.rule=Host(`${DOMAIN?}`)
        - traefik.http.routers.${PROJECT_NAME?}-https.entrypoints=https
        - traefik.http.routers.${PROJECT_NAME?}-https.tls=true
        - traefik.http.routers.${PROJECT_NAME?}-https.tls.certresolver=le
        - traefik.http.services.${PROJECT_NAME?}.loadbalancer.server.port=3000
...

This is quite lengthy, but if you're using editor on the New project page, you can just start typing “deploy” and hit Enter to get this whole piece autocompleted. You'll just need to fill out the port and possibly tweak the routing rules.

Let's also define GITEA__server__ROOT_URL so that Gitea knows where it lives:

services:
  server:
    ...
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
      - GITEA__server__ROOT_URL=https://${DOMAIN?}/
...

Step 3. Define the variables

Right below the Compose file editor, you'll find another one for Environment variables. Those will be substituted in the Compose file we made, and we have two of them:

  • PROJECT_NAME which will be added by Lunni automatically, and
  • DOMAIN which we need to define ourselves.

This is fairly straightforward. For example, if your domain will be gitea.example.net, just type:

DOMAIN="gitea.example.net"

Make sure that DOMAIN actually points to your server (e. g. use a CNAME record).

DOMAIN variable

DOMAIN is a special variable in Lunni. If it is defined on a project, Lunni will show a link to https://${DOMAIN} on the project page, so that you can quickly access the HTTP service.

Step 4. Hit deploy

This is it! Let's go through our changed Compose file once again:

version: "3"

volumes:
  gitea-data:
  postgres-data:
networks:
  gitea: { external: false }
  traefik-public: { external: true }

services:
  server:
    image: gitea/gitea:1.17.3
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
      - GITEA__server__ROOT_URL=https://${DOMAIN?}/
    restart: always
    networks: [gitea, traefik-public]
    volumes:
      - gitea-data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports: 
      # - "3000:3000"
      - "222:22"
    deploy:
      labels:
        - traefik.enable=true
        - traefik.docker.network=traefik-public
        - traefik.constraint-label=traefik-public
        - traefik.http.routers.${PROJECT_NAME?}-http.rule=Host(`${DOMAIN?}`)
        - traefik.http.routers.${PROJECT_NAME?}-http.entrypoints=http
        - traefik.http.routers.${PROJECT_NAME?}-http.middlewares=https-redirect
        - traefik.http.routers.${PROJECT_NAME?}-https.rule=Host(`${DOMAIN?}`)
        - traefik.http.routers.${PROJECT_NAME?}-https.entrypoints=https
        - traefik.http.routers.${PROJECT_NAME?}-https.tls=true
        - traefik.http.routers.${PROJECT_NAME?}-https.tls.certresolver=le
        - traefik.http.services.${PROJECT_NAME?}.loadbalancer.server.port=3000
    depends_on: [db]
  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks: [gitea]
    volumes:
      - postgres-data:/var/lib/postgresql/data

If everything looks alright, click Create project and you're good to go!

Unsupported options

Docker Swarm ignores some options, like restart or depends_on. You can safely leave those in (e. g. if you use your compose file for both local development and deployment). See the Compose file v3 reference for more information.

Lunni will warn you about these in a later version.