github.com/alloyci/alloy-runner@v1.0.1-0.20180222164613-925503ccafd6/docs/configuration/advanced-configuration.md (about)

     1  # Advanced configuration
     2  
     3  AlloyCI Runner configuration uses the [TOML][] format.
     4  
     5  The file to be edited can be found in:
     6  
     7  1. `/etc/alloy-runner/config.toml` on \*nix systems when alloy-runner is
     8     executed as root (**this is also path for service configuration**)
     9  1. `~/.alloy-runner/config.toml` on \*nix systems when alloy-runner is
    10     executed as non-root
    11  1. `./config.toml` on other systems
    12  
    13  ## The global section
    14  
    15  This defines global settings of AlloyCI Runner.
    16  
    17  | Setting | Description |
    18  | ------- | ----------- |
    19  | `concurrent`     | limits how many jobs globally can be run concurrently. The most upper limit of jobs using all defined runners. `0` **does not** mean unlimited |
    20  | `log_level`      | Log level (options: debug, info, warn, error, fatal, panic). Note that this setting has lower priority than log-level set by command line argument --debug, -l or --log-level |
    21  | `check_interval` | defines the interval length, in seconds, between new jobs check. The default value is `3`; if set to `0` or lower, the default value will be used. |
    22  | `sentry_dsn`     | enable tracking of all system level errors to sentry |
    23  | `metrics_server` | address (`<host>:<port>`) on which the Prometheus metrics HTTP server should be listening |
    24  
    25  Example:
    26  
    27  ```bash
    28  concurrent = 4
    29  log_level = "warning"
    30  ```
    31  
    32  ## The [[runners]] section
    33  
    34  This defines one runner entry.
    35  
    36  | Setting | Description |
    37  | ------- | ----------- |
    38  | `name`               | not used, just informatory |
    39  | `url`                | CI URL |
    40  | `token`              | runner token |
    41  | `tls-ca-file`        | file containing the certificates to verify the peer when using HTTPS |
    42  | `tls-cert-file`      | file containing the certificate to authenticate with the peer when using HTTPS |
    43  | `tls-key-file`       | file containing the private key to authenticate with the peer when using HTTPS |
    44  | `limit`              | limit how many jobs can be handled concurrently by this token. `0` (default) simply means don't limit |
    45  | `executor`           | select how a project should be built, see next section |
    46  | `shell`              | the name of shell to generate the script (default value is platform dependent) |
    47  | `builds_dir`         | directory where builds will be stored in context of selected executor (Locally, Docker, SSH) |
    48  | `cache_dir`          | directory where build caches will be stored in context of selected executor (Locally, Docker, SSH). If the `docker` executor is used, this directory needs to be included in its `volumes` parameter. |
    49  | `environment`        | append or overwrite environment variables |
    50  | `request_concurrency` | limit number of concurrent requests for new jobs from AlloyCI (default 1) |
    51  | `output_limit`       | set maximum build log size in kilobytes, by default set to 4096 (4MB) |
    52  | `pre_clone_script`   | commands to be executed on the runner before cloning the Git repository. this can be used to adjust the Git client configuration first, for example. To insert multiple commands, use a (triple-quoted) multi-line string or "\n" character. |
    53  | `pre_build_script`   | commands to be executed on the runner after cloning the Git repository, but before executing the build. To insert multiple commands, use a (triple-quoted) multi-line string or "\n" character. |
    54  | `post_build_script`  | commands to be executed on the runner just after executing the build, but before executing `after_script`. To insert multiple commands, use a (triple-quoted) multi-line string or "\n" character. |
    55  | `clone_url`	       | Overwrite the URL for the AlloyCI instance. Used if the runner can't connect to AlloyCI on the URL AlloyCI exposes itself. |
    56  
    57  Example:
    58  
    59  ```bash
    60  [[runners]]
    61    name = "ruby-2.1-docker"
    62    url = "https://CI/"
    63    token = "TOKEN"
    64    limit = 0
    65    executor = "docker"
    66    builds_dir = ""
    67    shell = ""
    68    environment = ["ENV=value", "LC_ALL=en_US.UTF-8"]
    69    clone_url = "http://alloy.example.local"
    70  ```
    71  
    72  ### How `clone_url` works
    73  
    74  In cases where the AlloyCI instance is exposed to an URL which can't be used
    75  by the runner, a `clone_url` can be configured. For example; AlloyCI is exposed
    76  to `https://alloy.example.com`, but the runner can't reach that because of
    77  a firewall setup. If the runner can reach the node on `192.168.1.23`,
    78  the `clone_url` should be set to `"http://192.168.1.23`.
    79  
    80  Only if the `clone_url` is set, the runner will construct a clone URL in the form
    81  of `http://alloy-ci-token:s3cr3tt0k3n@192.168.1.23/namespace/project.git`.
    82  
    83  ## The EXECUTORS
    84  
    85  There are a couple of available executors currently.
    86  
    87  | Executor | Description |
    88  | -------- | ----------- |
    89  | `shell`       | run build locally, default |
    90  | `docker`      | run build using Docker container - this requires the presence of `[runners.docker]` and [Docker Engine][] installed on the system that the Runner runs |
    91  | `docker-ssh`  | run build using Docker container, but connect to it with SSH - this requires the presence of `[runners.docker]` , `[runners.ssh]` and [Docker Engine][] installed on the system that the Runner runs. **Note: This will run the docker container on the local machine, it just changes how the commands are run inside that container. If you want to run docker commands on an external machine, then you should change the `host` parameter in the `runners.docker` section.**|
    92  | `ssh`         | run build remotely with SSH - this requires the presence of `[runners.ssh]` |
    93  | `parallels`   | run build using Parallels VM, but connect to it with SSH - this requires the presence of `[runners.parallels]` and `[runners.ssh]` |
    94  | `virtualbox`  | run build using VirtualBox VM, but connect to it with SSH - this requires the presence of `[runners.virtualbox]` and `[runners.ssh]` |
    95  | `docker+machine` | like `docker`, but uses [auto-scaled docker machines](autoscale.md) - this requires the presence of `[runners.docker]` and `[runners.machine]` |
    96  | `docker-ssh+machine` | like `docker-ssh`, but uses [auto-scaled docker machines](autoscale.md) - this requires the presence of `[runners.docker]` and `[runners.machine]` |
    97  | `kubernetes` | run build using Kubernetes Pods - this requires the presence of `[runners.kubernetes]` |
    98  
    99  ## The SHELLS
   100  
   101  There are a couple of available shells that can be run on different platforms.
   102  
   103  | Shell | Description |
   104  | ----- | ----------- |
   105  | `bash`        | generate Bash (Bourne-shell) script. All commands executed in Bash context (default for all Unix systems) |
   106  | `sh`          | generate Sh (Bourne-shell) script. All commands executed in Sh context (fallback for `bash` for all Unix systems) |
   107  | `cmd`         | generate Windows Batch script. All commands are executed in Batch context (default for Windows) |
   108  | `powershell`  | generate Windows PowerShell script. All commands are executed in PowerShell context |
   109  
   110  ## The [runners.docker] section
   111  
   112  This defines the Docker Container parameters.
   113  
   114  | Parameter | Description |
   115  | --------- | ----------- |
   116  | `host`                      | specify custom Docker endpoint, by default `DOCKER_HOST` environment is used or `unix:///var/run/docker.sock` |
   117  | `hostname`                  | specify custom hostname for Docker container |
   118  | `runtime`                   | specify a runtime for Docker container |
   119  | `tls_cert_path`             | when set it will use `ca.pem`, `cert.pem` and `key.pem` from that folder to make secure TLS connection to Docker (useful in boot2docker) |
   120  | `image`                     | use this image to run builds |
   121  | `cpuset_cpus`               | string value containing the cgroups CpusetCpus to use |
   122  | `cpus`                      | number of CPUs (available in docker 1.13 or later) |
   123  | `dns`                       | a list of DNS servers for the container to use |
   124  | `dns_search`                | a list of DNS search domains |
   125  | `privileged`                | make container run in Privileged mode (insecure) |
   126  | `userns_mode`               | Sets the usernamespace mode for the container when usernamespace remapping option is enabled. (available in docker 1.10 or later) |
   127  | `cap_add`                   | add additional Linux capabilities to the container |
   128  | `cap_drop`                  | drop additional Linux capabilities from the container |
   129  | `security_opt`              | set security options (--security-opt in docker run), takes a list of ':' separated key/values |
   130  | `devices`                   | share additional host devices with the container |
   131  | `disable_cache`             | disable use of automatically created (i.e., not mapped to a host directory) cache volumes |
   132  | `network_mode`              | add container to a custom network |
   133  | `wait_for_services_timeout` | specify how long to wait for docker services, set to 0 to disable, default: 30 |
   134  | `cache_dir`                 | specify where Docker caches should be stored (this can be absolute or relative to current working directory) |
   135  | `volumes`                   | specify additional volumes that should be mounted (same syntax as Docker -v option) |
   136  | `extra_hosts`               | specify hosts that should be defined in container environment |
   137  | `shm_size`                  | specify shared memory size for images (in bytes) |
   138  | `volumes_from`              | specify a list of volumes to inherit from another container in the form <code>\<container name\>[:\<ro&#124;rw\>]</code> |
   139  | `volume_driver`             | specify the volume driver to use for the container |
   140  | `links`                     | specify containers which should be linked with building container |
   141  | `services`                  | specify additional services that should be run with build. Please visit [Docker Registry](https://registry.hub.docker.com/) for list of available applications. Each service will be run in separate container and linked to the build. |
   142  | `allowed_images`            | specify wildcard list of images that can be specified in .alloy-ci.json. If not present all images are allowed (equivalent to `["*/*:*"]`) |
   143  | `allowed_services`          | specify wildcard list of services that can be specified in .alloy-ci.json. If not present all images are allowed (equivalent to `["*/*:*"]`) |
   144  | `pull_policy`               | specify the image pull policy: `never`, `if-not-present` or `always` (default); read more in the [pull policies documentation](../executors/docker.md#how-pull-policies-work) |
   145  | `sysctls`                   | specify the sysctl options |
   146  | `helper_image`              | [ADVANCED] Override the default helper image used to clone repos and upload artifacts |
   147  
   148  Example:
   149  
   150  ```bash
   151  [runners.docker]
   152    host = ""
   153    hostname = ""
   154    tls_cert_path = "/Users/ayufan/.boot2docker/certs"
   155    image = "ruby:2.1"
   156    cpuset_cpus = "0,1"
   157    dns = ["8.8.8.8"]
   158    dns_search = [""]
   159    privileged = false
   160    userns_mode = "host"
   161    cap_add = ["NET_ADMIN"]
   162    cap_drop = ["DAC_OVERRIDE"]
   163    devices = ["/dev/net/tun"]
   164    disable_cache = false
   165    wait_for_services_timeout = 30
   166    cache_dir = ""
   167    volumes = ["/data", "/home/project/cache"]
   168    extra_hosts = ["other-host:127.0.0.1"]
   169    shm_size = 300000
   170    volumes_from = ["storage_container:ro"]
   171    links = ["mysql_container:mysql"]
   172    services = ["mysql", "redis:2.8", "postgres:9"]
   173    allowed_images = ["ruby:*", "python:*", "php:*"]
   174    allowed_services = ["postgres:9.4", "postgres:latest"]
   175    [runners.docker.sysctls]
   176      "net.ipv4.ip_forward" = "1"
   177  ```
   178  
   179  ### Volumes in the [runners.docker] section
   180  
   181  You can find the complete guide of Docker volume usage
   182  [here](https://docs.docker.com/userguide/dockervolumes/).
   183  
   184  Let's use some examples to explain how it work (assuming you have a working
   185  runner).
   186  
   187  #### Example 1: adding a data volume
   188  
   189  A data volume is a specially-designated directory within one or more containers
   190  that bypasses the Union File System. Data volumes are designed to persist data,
   191  independent of the container's life cycle.
   192  
   193  ```bash
   194  [runners.docker]
   195    host = ""
   196    hostname = ""
   197    tls_cert_path = "/Users/ayufan/.boot2docker/certs"
   198    image = "ruby:2.1"
   199    privileged = false
   200    disable_cache = true
   201    volumes = ["/path/to/volume/in/container"]
   202  ```
   203  
   204  This will create a new volume inside the container at `/path/to/volume/in/container`.
   205  
   206  #### Example 2: mount a host directory as a data volume
   207  
   208  In addition to creating a volume using you can also mount a directory from your
   209  Docker daemon's host into a container. It's useful when you want to store
   210  builds outside the container.
   211  
   212  ```bash
   213  [runners.docker]
   214    host = ""
   215    hostname = ""
   216    tls_cert_path = "/Users/ayufan/.boot2docker/certs"
   217    image = "ruby:2.1"
   218    privileged = false
   219    disable_cache = true
   220    volumes = ["/path/to/bind/from/host:/path/to/bind/in/container:rw"]
   221  ```
   222  
   223  This will use `/path/to/bind/from/host` of the CI host inside the container at
   224  `/path/to/bind/in/container`.
   225  
   226  ### Using a private container registry
   227  
   228  > **Notes:**
   229  - This feature requires AlloyCI Runner **1.0** or higher
   230  - Using private registries with the `if-not-present` pull policy may introduce
   231    [security implications][secpull]. To fully understand how pull policies work,
   232    read the [pull policies documentation](../executors/docker.md#how-pull-policies-work).
   233  
   234  If you want to use private registries as a source of images for your builds,
   235  you can set the authorization configuration in the `DOCKER_AUTH_CONFIG`
   236  [secret variable]. It can be set in both AlloyCI Variables section of
   237  a project and in the `config.toml` file.
   238  
   239  For a detailed example, visit the [Using Docker images documentation][priv-example].
   240  
   241  The steps performed by the Runner can be summed up to:
   242  
   243  1. The registry name is found from the image name.
   244  1. If the value is not empty, the executor will search for the authentication
   245     configuration for this registry.
   246  1. Finally, if an authentication corresponding to the specified registry is
   247     found, subsequent pulls will make use of it.
   248  
   249  Now that the Runner is set up to authenticate against your private registry,
   250  learn [how to configure .alloy-ci.json][json-priv-reg] in order to use that
   251  registry.
   252  
   253  #### Support for AlloyCI integrated registry
   254  
   255  Starting with AlloyCI v1.0, AlloyCI will send credentials for its integrated
   256  registry along with the build data. These credentials will be automatically
   257  added to registries authorization parameters list.
   258  
   259  After this authorization against the registry will be proceed like for
   260  configuration added with `DOCKER_AUTH_CONFIG` variable.
   261  
   262  Thanks to this, in your builds you can use any image from you AlloyCI integrated
   263  registry, even if the image is private/protected.
   264  
   265  #### Precedence of Docker authorization resolving
   266  
   267  As described above, AlloyCI Runner can authorize Docker against a registry by
   268  using credentials sent in different way. To find a proper registry, the following
   269  precedence is taken into account:
   270  
   271  1. Credentials configured with `DOCKER_AUTH_CONFIG`.
   272  1. Credentials configured locally on Runner's host with `~/.docker/config.json`
   273     or `~/.dockercfg` files (e.g., by running `docker login` on the host).
   274  1. Credentials sent by default with job's payload (e.g., credentials for _integrated
   275     registry_ described above).
   276  
   277  The first found credentials for the registry will be used. So for example,
   278  if you add some credentials for the _integrated registry_ with the
   279  `DOCKER_AUTH_CONFIG` variable, then the default credentials will be overridden.
   280  
   281  #### Restrict `allowed_images` to private registry
   282  
   283  For certain setups you will restrict access of the build jobs to docker images
   284  which comes from your private docker registry. In that case set
   285  
   286  ```bash
   287  [runners.docker]
   288    ...
   289    allowed_images = ["my.registry.tld:5000/*:*"]
   290  ```
   291  
   292  ## The [runners.parallels] section
   293  
   294  This defines the Parallels parameters.
   295  
   296  | Parameter | Description |
   297  | --------- | ----------- |
   298  | `base_name`         | name of Parallels VM which will be cloned |
   299  | `template_name`     | custom name of Parallels VM linked template (optional) |
   300  | `disable_snapshots` | if disabled the VMs will be destroyed after build |
   301  
   302  Example:
   303  
   304  ```bash
   305  [runners.parallels]
   306    base_name = "my-parallels-image"
   307    template_name = ""
   308    disable_snapshots = false
   309  ```
   310  
   311  ## The [runners.virtualbox] section
   312  
   313  This defines the VirtualBox parameters. This executor relies on
   314  `vboxmanage` as executable to control VirtualBox machines so you have to adjust
   315  your `PATH` environment variable on Windows hosts:
   316  `PATH=%PATH%;C:\Program Files\Oracle\VirtualBox`.
   317  
   318  | Parameter | Explanation |
   319  | --------- | ----------- |
   320  | `base_name`         | name of VirtualBox VM which will be cloned |
   321  | `base_snapshot`     | name or UUID of a specific snapshot of the VM from which to create a linked clone. If this is empty or omitted, the current snapshot will be used. If there is no current snapshot, one will be created unless `disable_snapshots` is true, in which case a full clone of the base VM will be made. |
   322  | `disable_snapshots` | if disabled the VMs will be destroyed after build |
   323  
   324  Example:
   325  
   326  ```bash
   327  [runners.virtualbox]
   328    base_name = "my-virtualbox-image"
   329    base_snapshot = "my-image-snapshot"
   330    disable_snapshots = false
   331  ```
   332  
   333  ## The [runners.ssh] section
   334  
   335  This defines the SSH connection parameters.
   336  
   337  | Parameter  | Description |
   338  | ---------- | ----------- |
   339  | `host`     | where to connect (overridden when using `docker-ssh`) |
   340  | `port`     | specify port, default: 22 |
   341  | `user`     | specify user |
   342  | `password` | specify password |
   343  | `identity_file` | specify file path to SSH private key (id_rsa, id_dsa or id_edcsa). The file needs to be stored unencrypted |
   344  
   345  Example:
   346  
   347  ```
   348  [runners.ssh]
   349    host = "my-production-server"
   350    port = "22"
   351    user = "root"
   352    password = "production-server-password"
   353    identity_file = ""
   354  ```
   355  
   356  ## The [runners.machine] section
   357  
   358  This defines the Docker Machine based autoscaling feature. More details can be
   359  found in the separate [runners autoscale documentation](autoscale.md).
   360  
   361  | Parameter           | Description |
   362  |---------------------|-------------|
   363  | `IdleCount`         | Number of machines, that need to be created and waiting in _Idle_ state. |
   364  | `IdleTime`          | Time (in seconds) for machine to be in _Idle_ state before it is removed. |
   365  | `OffPeakPeriods`    | Time periods when the scheduler is in the OffPeak mode. An array of cron-style patterns (described below). |
   366  | `OffPeakTimezone`   | Time zone for the times given in OffPeakPeriods. A timezone string like Europe/Berlin (defaults to the locale system setting of the host if omitted or empty). |
   367  | `OffPeakIdleCount`  | Like `IdleCount`, but for _Off Peak_ time periods. |
   368  | `OffPeakIdleTime`   | Like `IdleTime`, but for _Off Peak_ time mperiods. |
   369  | `MaxBuilds`         | Builds count after which machine will be removed. |
   370  | `MachineName`       | Name of the machine. It **must** contain `%s`, which will be replaced with a unique machine identifier. |
   371  | `MachineDriver`     | Docker Machine `driver` to use. More details can be found in the [Docker Machine configuration section](autoscale.md#what-are-the-supported-cloud-providers). |
   372  | `MachineOptions`    | Docker Machine options. More details can be found in the [Docker Machine configuration section](autoscale.md#what-are-the-supported-cloud-providers). |
   373  
   374  Example:
   375  
   376  ```bash
   377  [runners.machine]
   378    IdleCount = 5
   379    IdleTime = 600
   380    OffPeakPeriods = [
   381      "* * 0-10,18-23 * * mon-fri *",
   382      "* * * * * sat,sun *"
   383    ]
   384    OffPeakTimezone = "Europe/Berlin"
   385    OffPeakIdleCount = 1
   386    OffPeakIdleTime = 3600
   387    MaxBuilds = 100
   388    MachineName = "auto-scale-%s"
   389    MachineDriver = "digitalocean"
   390    MachineOptions = [
   391        "digitalocean-image=coreos-stable",
   392        "digitalocean-ssh-user=core",
   393        "digitalocean-access-token=DO_ACCESS_TOKEN",
   394        "digitalocean-region=nyc2",
   395        "digitalocean-size=4gb",
   396        "digitalocean-private-networking",
   397        "engine-registry-mirror=http://10.11.12.13:12345"
   398    ]
   399  ```
   400  
   401  ### OffPeakPeriods syntax
   402  
   403  The `OffPeakPeriods` setting contains an array of string patterns of
   404  time periods represented in a cron-style format. The line contains
   405  following fields:
   406  
   407  ```
   408  [second] [minute] [hour] [day of month] [month] [day of week] [year]
   409  ```
   410  
   411  Like in the standard cron configuration file, the fields can contain single
   412  values, ranges, lists and asterisks. A detailed description of the syntax
   413  can be found [here][cronvendor].
   414  
   415  ## The [runners.cache] section
   416  
   417  This defines the distributed cache feature. More details can be found
   418  in the [runners autoscale documentation](autoscale.md#distributed-runners-caching).
   419  
   420  | Parameter        | Type             | Description |
   421  |------------------|------------------|-------------|
   422  | `Type`           | string           | As of now, only S3-compatible services are supported, so only `s3` can be used. |
   423  | `ServerAddress`  | string           | A `host:port` to the used S3-compatible server. |
   424  | `AccessKey`      | string           | The access key specified for your S3 instance. |
   425  | `SecretKey`      | string           | The secret key specified for your S3 instance. |
   426  | `BucketName`     | string           | Name of the bucket where cache will be stored. |
   427  | `BucketLocation` | string           | Name of S3 region. |
   428  | `Insecure`       | boolean          | Set to `true` if the S3 service is available by `HTTP`. Is set to `false` by default. |
   429  | `Path`           | string           | Name of the path to prepend to the cache URL. |
   430  | `Shared`         | boolean          | Enables cache sharing between runners, `false` by default. |
   431  
   432  Example:
   433  
   434  ```bash
   435  [runners.cache]
   436    Type = "s3"
   437    ServerAddress = "s3.amazonaws.com"
   438    AccessKey = "AMAZON_S3_ACCESS_KEY"
   439    SecretKey = "AMAZON_S3_SECRET_KEY"
   440    BucketName = "runners"
   441    BucketLocation = "eu-west-1"
   442    Insecure = false
   443    Path = "path/to/prefix"
   444    Shared = false
   445  ```
   446  
   447  > **Note:** For Amazon's S3 service the `ServerAddress` should always be `s3.amazonaws.com`. Minio S3 client will
   448  > get bucket metadata and modify the URL to point to the valid region (eg. `s3-eu-west-1.amazonaws.com`) itself.
   449  
   450  > **Note:** If any of `ServerAddress`, `AccessKey` or `SecretKey` aren't specified then the S3 client will use the
   451  > IAM instance profile available to the instance.
   452  
   453  ## The [runners.kubernetes] section
   454  
   455  This defines the Kubernetes parameters.
   456  See [Kubernetes executor](../executors/kubernetes.md) for additional parameters.
   457  
   458  | Parameter        | Type    | Description |
   459  |------------------|---------|-------------|
   460  | `host`           | string  | Optional Kubernetes master host URL (auto-discovery attempted if not specified) |
   461  | `cert_file`      | string  | Optional Kubernetes master auth certificate |
   462  | `key_file`       | string  | Optional Kubernetes master auth private key |
   463  | `ca_file`        | string  | Optional Kubernetes master auth ca certificate |
   464  | `image`          | string  | Default docker image to use for builds when none is specified |
   465  | `namespace`      | string  | Namespace to run Kubernetes jobs in |
   466  | `privileged`     | boolean | Run all containers with the privileged flag enabled |
   467  | `node_selector`  | table   | A `table` of `key=value` pairs of `string=string`. Setting this limits the creation of pods to kubernetes nodes matching all the `key=value` pairs |
   468  | `image_pull_secrets` | array | A list of secrets that are used to authenticate docker image pulling |
   469  
   470  Example:
   471  
   472  ```bash
   473  [runners.kubernetes]
   474  	host = "https://45.67.34.123:4892"
   475  	cert_file = "/etc/ssl/kubernetes/api.crt"
   476  	key_file = "/etc/ssl/kubernetes/api.key"
   477  	ca_file = "/etc/ssl/kubernetes/ca.crt"
   478  	namespace = "alloy-ci"
   479  	image = "golang:1.8"
   480  	privileged = true
   481  	image_pull_secrets = ["docker-registry-credentials"]
   482  	[runners.kubernetes.node_selector]
   483  		alloy-ci = "true"
   484  ```
   485  
   486  ## Note
   487  
   488  If you'd like to deploy to multiple servers using AlloyCI, you can create a
   489  single script that deploys to multiple servers or you can create many scripts.
   490  It depends on what you'd like to do.
   491  
   492  [TOML]: https://github.com/toml-lang/toml
   493  [Docker Engine]: https://www.docker.com/docker-engine
   494  [json-priv-reg]: ../../json/README.md#image-and-services
   495  [secpull]: ../security/README.md#usage-of-private-docker-images-with-if-not-present-pull-policy
   496  [priv-example]: ../../docker/README.md#define-an-image-from-a-private-docker-registry
   497  [secret variable]: https://docs.gitlab.com/ce/ci/variables/#secret-variables
   498  [cronvendor]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/vendor/github.com/gorhill/cronexpr/README.md