> For the complete documentation index, see [llms.txt](https://docs.edgegap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edgegap.com/docs/tools-and-integrations/docker.md).

# Docker & Containers

Why Edgegap runs game servers in containers, and how to get Docker set up before building your first server image.

{% columns %}
{% column width="25%" %}

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fgit-blob-e6111f686dccc8bebc29f2f10fe2ce093f9c2b9c%2Fdocker.png?alt=media" alt="" width="100"><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="75%" %}
Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly.
{% endcolumn %}
{% endcolumns %}

## Why use Docker?

Working in a team of developers means sharing your code. When things go wrong, the last thing you want to hear is “it works on my machine”. Game servers have to run reliably on any machine, since a successful games’ servers will run on thousands of server machines across the world.

To help make your server reliable, we use Docker - virtualization software to ensuring that all of your server code dependencies down to the operating system level are going to be always exactly the same, no matter how or where the server is launched.

{% hint style="info" %}
We recommend watching ["Never install locally" (video)](https://www.youtube.com/watch?v=J0NuOlA2xDc\&ab_channel=Coderized). **You DON'T need to use Dockerhub with Docker**.  Docker ≠ Dockerhub. Think of Docker as a programming engine and Dockerhub as it’s App Store.
{% endhint %}

## Containers

A **container is a standard unit of software that packages up code** and all its dependencies so the application runs quickly and reliably from one computing environment to another. Containers isolate software from its environment and ensure that it works uniformly despite differences for instance between development and staging.

\
Once you run packaged docker images, they become:

* **Containers** if running in Docker Desktop or Docker Engine (usually locally),
* &#x20;[Deployments](/learn/orchestration/deployments.md) if running on Edgegap (both Edge Cloud and Private Fleet).

A container runs natively on Linux (or in Docker desktop) and shares the host machine's kernel with other containers. It runs a discrete process, taking no more memory than any further executable, making it lightweight.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fgit-blob-f150d345ddba740a7babd5338aca3f557cc1886c%2Fcontainer-architecture.png?alt=media" alt=""><figcaption></figcaption></figure>

## **Try it Out**

To use docker and build or run docker images locally, you'll need to install Docker:

<details>

<summary><a href="https://www.docker.com/products/docker-desktop/">Install Docker Desktop (or Docker CLI)</a></summary>

* [Install Docker Desktop from the official source](https://www.docker.com/products/docker-desktop/) (no account required).
* Restart your computer after completing the installation.

</details>

Once installed, you can open your terminal of choice and try running a test image.

```sh
docker run -d -p 80:80 --name speedtest edgegap/speedtest-edge
```

This will automatically perform the following steps:

1. Download the speedtest image from Edgegap Repository (only the first time).
2. Create and start a new container using this image on your machine.
3. Assign the name "speedtest" (using `--name` parameter).
4. Expose a [Apps and Versions](/learn/orchestration/application-and-versions.md#port-mapping) to connect your machine's port `80`  to the container's port `80` , enabling access to the container from your machine.

✅ Open [http://localhost:80](http://localhost/) in your preferred browser to confirm the container started OK.

With a Container running, we can perform basic interactions - restart, stop, monitor.&#x20;

First, let's see what is running on your docker engine using your preferred terminal.

```sh
docker ps
```

This will print information about running containers, for example:

```
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                NAMES
dbf4d3734111        edgegap/speedtest-edge   "docker-php-entrypoi…"   29 minutes ago      Up 29 minutes       0.0.0.0:80->80/tcp   speedtest
```

You can have any number of containers running.

Let's try to get more information about your container:

```sh
docker container inspect speedtest
```

You can also inspect the network shared between your machine and the containers.

```sh
docker network ls
```

This will print information about the network ran by docker engine on your machine.

```
NETWORK ID          NAME                DRIVER              SCOPE
67b24038b9ca        bridge              bridge              local
6f7fe43a489d        host                host                local
3d80cdb86a7e        none                null                local
```

You can also inspect individual networks closer:

```sh
docker network inspect 67b24038b9ca
# where 678b24038b9ca is your network connected to your Container
```

Once you're done with testing the container, make sure to stop it to regain the resources (CPU and RAM) which the container was using to run:

```sh
docker container stop speedtest
```

## SSH tunnel

To troubleshoot server issues in depth, you can expose the SSH service in your images and connect to the container or deployment just the same as any remote host or a VM.

{% hint style="danger" %}
**Never expose SSH service in production deployments** - it's a serious security issue.
{% endhint %}

### Test Locally

We'll need to make some modifications to your Dockerfile. Let's install OpenSSH service, create a user with a password, and enable the service.

{% hint style="success" %}
**Order of Dockerfile commands matters!** [Docker caches layers to speed up your builds.](https://docs.docker.com/build/cache/) Keep uncached layers like `RUN` and `COPY` at the end of your Dockerfile (before `CMD`).
{% endhint %}

Merge these Dockerfile commands in your Dockerfile:

{% tabs %}
{% tab title="Ubuntu base image" %}

```dockerfile
RUN apt-get update
RUN apt-get install openssh-server sudo -y

RUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u 1000
RUN echo 'tester:$ecureP@ss' | chpasswd
# don't forget to change your password ^

EXPOSE 22
```

{% endtab %}

{% tab title="Alpine base image" %}

```dockerfile
RUN apk --update add --no-cache openssh bash
RUN sed -i s/#PermitRootLogin.*/PermitRootLogin\ yes/ /etc/ssh/sshd_config

RUN echo "tester:$ecureP@ss" | chpasswd
# don't forget to change your password ^

EXPOSE 22
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
`EXPOSE` doesn't automatically publish ports, it's a hint for docker engine and developers.
{% endhint %}

We'll also need to start the ssh service in the container:

{% tabs %}
{% tab title="Starting from Dockerfile (Unity Plugin)" %}
{% code title="Dockerfile" %}

```docker
CMD [ \
  "/bin/bash", "-c", \
  "env;service ssh start;/path/to/server-executable" \
]
# prints env variables, starts SSH, then starts game server
```

{% endcode %}
{% endtab %}

{% tab title="Starting from a .sh shell script (Unreal)" %}
{% code title="StartServer.sh" %}

```sh
service ssh start
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
You can't start the SSH service using a `RUN` command, since it's only excuted at build time.
{% endhint %}

✅ Now `docker build`, and test `docker run`  locally, then test with `ssh localhost:22`.

{% hint style="danger" %}
:x: **DO NOT - overwrite existing tags or use `latest` tag** - this will deploy an outdated cache.\
:white\_check\_mark: **DO - always increase your version tag** and deploy the new build.
{% endhint %}

{% hint style="success" %}
**Troubleshoot issues locally (and safely) first, without pushing the image to registry.**
{% endhint %}

### Deploy in Cloud

If you didn't succeed with [#test-locally](#test-locally "mention"), you can test in a Deployment environment.

✅ [Log in to your docker registry](https://app.edgegap.com/registry-management/repositories/list) and  `docker push`  the new tag:

{% code title="" %}

```sh
username='robot$<REGISTRY_ORGANIZATION_ID>+client-push';
token='<REGISTRY_TOKEN>';

project='<REGISTRY_PROJECT>';
image='<MY_IMAGE_NAME>';
tag='<MY_TAG>';

echo "$token" | docker login registry.edgegap.com -u "$username" --password-stdin 2>&1;
docker push "${registry}/${project}/${image}:${tag}";
```

{% endcode %}

✅ [Create a new testing app version for this image through dashboard.](https://app.edgegap.com/application-management/applications/list)

* Add a port mapping for 22/TCP to mirror the `-p` option from `docker run`.

✅ [Deploy](https://app.edgegap.com/deployment-management/deployments/create) and use the URL or IP with the external SSH port assigned to `ssh`.

✅ Once you're done testing, stop the deployment and delete your testing image.
