Self-hosting with nginx + docker-compose

Published on

Self-hosting applications and services has never been easier.

Here I want to outline how I deploy and manage my self-hosted services on Linode with nginx, docker-compose and CloudFlare


  • create a nginx site configuration under /etc/nginx/site-enabled/ with the correct port mapping and domain
  • verify restart: always is set in your docker-compose.yml
  • docker-compose up
  • provision SSL certificate with CloudFlare / alternative with Letsencrypt

Get a cheap VPS

There are many services that offer virtual private servers for quite cheap prices.

The providers I use are Linode and DigitalOcean (affiliate links, we both get a commission if you use these links)

Finding self-hostable services

awesome-selfhosted is THE resource to find new projects.

Some are outdated, some are discontinued, many are up to date and also “docker-friendly”.

The general idea is:

  • browse through the categories
  • find the service you want to self-host
  • check whether a docker-compose.yml file is present

If the project you want to self-host doesn’t have a docker-compose.yml file, you can try to create your own.

Alternatively, if the service doesn’t depend on other containers (e.g. persistence) you can try to spin it up directly with the docker cli and provide the --restart=always flag.

Docker / docker-compose

If the project contains a docker-compose.yml you’re ready to clone it.

Just git clone the repo and cd into it.

Understand with the help of the port mapping in the docker-compose.yml file, on which port the service is exposed, and especially on which network:

If you find a port mapping like this:

    - 8080:8080

you might want to expose it only on the local network (because by default it exposes it on


If there is no explicit port mapping, you might need to understand on which port the service is exposed.

Oftentimes, you can customize the HTTP port through some env var defined in the docker-compose.yml file.

    - PORT=3000

To start your service just run docker-compose up -d (-d so that the service will run as a daemon, in the background)

Now it’s time to set up a reverse proxy with nginx.


First of all, install nginx on your VPS.

Now take the snippet below and change it accordingly to your needs:

You’ll need to customize

  • YOUR_PORT: the HTTP port on which the service is exposed through docker, e.g. 3000
  • YOUR_SUBDOMAIN: define a sub-domain on which the service will be reachable, e.g. stats
  • YOUR_TLD: your top-level domain, e.g.
  • your_service: change this to a meaningful name of your service, e.g. stats
upstream your_service {
    server max_fails=5 fail_timeout=60s;

server {
    server_name    YOUR_SUBDOMAIN.YOUR_TLD;

    listen         80;
    listen         [::]:80;

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;

    client_max_body_size 16m;
    ignore_invalid_headers off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://your_service;

Put this file under /etc/nginx/sites-available/<service>.

Then link it with ln -s /etc/nginx/sites-available/<service> /etc/nginx/sites-enabled/<service>

Also check that everything the nginx configuration is valid with nginx -t

SSL / CloudFlare

Now I encourage you to put a SSL certificate in front of your service.

The easiest way I found is to use CloudFlare to issue a free SSL certificate for my domains.

To configure Cloudflare you’ll need to log in to your domain registrar and transfer the DNS records to CloudFlare. CloudFlare provides excellent guides to do this.

Now simply get your VPS IP address and create a new DNS record in the CloudFlare dashboard.

Create a A record that points your subdomain to your VPS IP address.

Check that the “Proxy status” is set to “Proxied” so that CloudFlare does the SSL termination.


Recently I wrote about what services I am self-hosting, here you can find practical examples I use daily.

Here, have a slice of pizza 🍕