Dynamic Compose
In Runtipi version v3.2.0
we added a new feature called dynamic compose, this is a
simplified and custom version of the docker-compose.yml
file that allows for more control
on how apps gets deployed, for example only using the reverse proxy or only using ports,
it also allows extra features like multiple appstores. This feature is still quite new
and we are trying to migrate the entire appstore to the new format deprecating the
docker-compose.yml
file.
Here is an example docker-compose.json
file for the app Nginx:
{
"services": [
{
"name": "nginx",
"image": "nginx",
"internalPort": 80,
"isMain": true
}
]
}
And that's it, with just 10 lines of json you can deploy an nginx app.
Creating the dynamic compose file
Let's see how easy it is to create the docker-compose.json
file.
Creating the file and enabling dynamic compose
The first thing we need to do is create the docker-compose.json
next to the docker-compose.yml
file
in the app folder. We also need to tell Runtipi to use the file so we need to edit the app's config.json
and add the following configuration line at the end:
"dynamic_config": true
Defining the services
Now we need to add our services
array which defines all our services.
{
"services": []
}
The services
key is the same as in the docker-compose.yml
, inside this array we can add multiple apps.
Adding a service
Let's add our myapp
service.
{
"services": [
{
"name": "myapp"
}
]
}
As you can see I created an object and added the name
key, that defines the service name and container name.
Adding an image
Let's add an image now, for this example our image is myapp:latest
.
{
"services": [
{
"name": "app",
"image": "myapp:latest"
}
]
}
Exposing the UI
In this example this service also exposes a web UI, so let's add that too.
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true
}
]
}
So we defined our app port using the internalPort
key, the isMain
key indicates where the traefik labels should go,
traefik labels should always be on the part of the app that exposes the UI.
Adding volumes
In this example we also need to store some app data, we want to bind the ${APP_DATA_DIR}/data/myapp
host folder to
the /data
container folder with read-write permissions, this can be done like this:
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/myapp",
"containerPath": "data",
"readOnly": false
}
]
}
]
}
As you can see the volumes
key is an array where you can define multiple volumes in the form of the object we showcased
above.
Adding environment variables
Another important thing in a docker compose file are the environment variables, environment variables can be added very easily using this simple format:
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/myapp",
"containerPath": "data",
"readOnly": false
}
],
"environment": {
"FOO": "bar",
"PASSWORD": "${MYAPP_PASSWORD}"
}
}
]
}
You can add as many environment variables as you like and you can also use the values from form fields as you normally would.
Adding multiple ports
A lot of apps require more that one port, either an API or a setup UI, you can add multiple ports like this:
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/myapp",
"containerPath": "data",
"readOnly": false
}
],
"environment": {
"FOO": "bar",
"PASSWORD": "${MYAPP_PASSWORD}"
},
"addPorts": [
{
"containerPort": 8080,
"hostPort": 8080,
"tcp": true
},
{
"containerPort": 25565,
"hostPort": 25565,
"udp": true
}
]
}
]
}
Again we create an addPorts
array and define our container and host port, you can specify if the port is UDP or TCP,
if you don't define anything TCP will be assumed. If you need to add a ports that does both TCP and UDP you can do it like so:
"addPorts": [
{
"containerPort": 8080, "hostPort": 8080, "tcp": true
},
{
"containerPort": 8080, "hostPort": 8080, "udp": true
}
]
Adding a healthcheck
If you like you can add a healthcheck to your app, this is exactly the same as in the docker-compose.yml
file
but in JSON format. Here is an example:
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/myapp",
"containerPath": "data",
"readOnly": false
}
],
"environment": {
"FOO": "bar",
"PASSWORD": "${MYAPP_PASSWORD}"
},
"addPorts": [
{
"containerPort": 8080,
"hostPort": 8080,
"tcp": true
},
{
"containerPort": 25565,
"hostPort": 25565,
"udp": true
}
],
"healthCheck": {
"test": "curl --fail http://localhost || exit 1",
"retries": 3,
"interval": "30s",
"timeout": "10s"
}
}
]
}
Depeneds on
A lot of services depend on other services (like a database) to be ready when they start. You can add the dependsOn
key
like this:
{
"services": [
{
"name": "myapp",
"image": "myapp:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/myapp",
"containerPath": "data",
"readOnly": false
}
],
"environment": {
"FOO": "bar",
"PASSWORD": "${MYAPP_PASSWORD}"
},
"addPorts": [
{
"containerPort": 8080,
"hostPort": 8080,
"tcp": true
},
{
"containerPort": 25565,
"hostPort": 25565,
"udp": true
}
],
"healthCheck": {
"test": "curl --fail http://localhost || exit 1",
"retries": 3,
"interval": "30s",
"timeout": "10s"
},
"dependsOn": {
"service1": {
"condition": "service_healthy"
}
}
}
]
}
If you don't need to specify the condition you can add a simpler format like this:
"dependsOn": "service1"
Miscellaneous
Apart from the most common docker compose items, you can also add some other not so often ones.
For example a custom command:
"command": "/my/app"
A custom network mode:
"networkMode": "host"
Ulimits:
"ulimits": {
"nproc": {
"soft": 10,
"hard": 20
},
"nofile": {
"soft": 20,
"hard": 30
}
}
Extra hosts:
"extraHosts": [
"somehost:someotherhost"
]