Self-Hosting services at home under Docker

Self-Hosting services at home under Docker
Plainshawk Docker Services

Building a Self-Hosted Server on a Mac Mini with Docker

Mac Mini · Docker Desktop · Docker Compose · Nginx Proxy Manager · Self-Hosted Services

A modern Mac Mini makes an excellent small self-hosted server.

It is quiet, compact, energy efficient and powerful enough to run a surprisingly large collection of services simultaneously. Combined with Docker, it is possible to build a complete personal server environment without dedicating a room to conventional server hardware.

This is the foundation of my self-hosted environment.

The Mac Mini runs Docker Desktop, with each service isolated in its own Docker container. Nginx Proxy Manager provides the common HTTPS gateway, while a shared Docker network allows the containers to communicate directly without exposing unnecessary ports to the host or the internet.

The result is a small private cloud providing file storage, photography, documents, books, passwords, monitoring, collaboration, email relay and a personal blog — all running on one machine.

This article describes the initial Docker installation and configuration. The individual services are covered in the linked articles below.


The Overall Architecture

The basic architecture looks like this:

                         INTERNET
                            │
                     Cloudflare DNS
                            │
                    Router / Firewall
                       Ports 80/443
                            │
                            ▼
              ┌─────────────────────────┐
              │  Nginx Proxy Manager    │
              │       HTTPS Gateway     │
              │                         │
              │  Let's Encrypt SSL/TLS  │
              └────────────┬────────────┘
                           │
                     proxy-net
                           │
       ┌───────────────────┼───────────────────┐
       │                   │                   │
       ▼                   ▼                   ▼
   Nextcloud            Immich             Ghost
   Collabora          Immich Kiosk       Blog Server
       │                   │                   │
       └───────────────┬───┴───────────────────┘
                       │
              Other Docker Services
                       │
       ┌───────────────┼────────────────────┐
       ▼               ▼                    ▼
   Vaultwarden      Paperless            Homebox
   Passwords        Documents            Inventory

       ┌───────────────┼────────────────────┐
       ▼               ▼                    ▼
     Calibre        Uptime Kuma          Mail Relay
   Book Library     Monitoring       Postfix → Brevo

The important principle is that Docker provides the isolation and service management, while Nginx Proxy Manager provides the common external gateway.

Most containers do not need a host port at all. They communicate over Docker's internal network and are reached externally through Nginx Proxy Manager.


Part 1 — Installing Docker on the Mac Mini

Stage 1 — Install Docker Desktop

The simplest approach on macOS is Docker Desktop.

Download and install Docker Desktop for Mac, selecting the Apple Silicon version for an M-series Mac.

After installation, start Docker Desktop and allow it to complete its initial configuration.

Verify that Docker is working:

docker version

You should see both a Client and Server section.

Then check Docker Compose:

docker compose version

Modern Docker Desktop includes Docker Compose, so there is normally no need to install Compose separately.


Stage 2 — Configure Docker Desktop

Before installing any applications, configure Docker Desktop appropriately for a server that will run continuously.

Open:

Docker Desktop → Settings

The exact settings vary slightly between Docker Desktop releases, but the important considerations are:

Resources

Allocate enough CPU and memory for the services you intend to run.

Do not simply allocate everything available to Docker. macOS still needs resources for itself and for applications such as Terminal, Finder, browsers and backup software.

The correct allocation depends on the number and type of containers being run.

Services such as Immich, Nextcloud and Collabora can consume considerably more resources than lightweight services such as Uptime Kuma or Vaultwarden.

Start Docker Desktop at Login

Enable Docker Desktop to start automatically when the Mac starts.

This is particularly important if the Mac is being used as a server rather than as a conventional desktop computer.

However, starting Docker is not the same as starting the applications in the correct order. That is dealt with later using a startup script.


Part 2 — Create the Docker Directory Structure

I keep the Docker configuration for each application in its own directory.

For example:

~/docker/
├── mailrelay/
├── npm/
├── uptimekuma/
├── ghost/
├── nextcloud/
├── collabora/
├── immich/
├── immich-kiosk/
├── vaultwarden/
├── homebox/
├── calibre/
└── paperless/

Create the top-level directory:

mkdir -p ~/docker

Each application then gets its own directory:

mkdir -p ~/docker/mailrelay
mkdir -p ~/docker/npm
mkdir -p ~/docker/uptimekuma
mkdir -p ~/docker/ghost
mkdir -p ~/docker/nextcloud
mkdir -p ~/docker/collabora
mkdir -p ~/docker/immich
mkdir -p ~/docker/immich-kiosk
mkdir -p ~/docker/vaultwarden
mkdir -p ~/docker/homebox
mkdir -p ~/docker/calibre
mkdir -p ~/docker/paperless

Keeping applications separate makes updates, backups and troubleshooting considerably easier.


Part 3 — Understand Docker Compose

Each service is normally described by a docker-compose.yml file.

For example:

services:
  example:
    image: example/image:latest
    container_name: example
    restart: unless-stopped

The Compose file defines:

  • the Docker image
  • the container name
  • persistent storage
  • environment variables
  • networks
  • dependencies
  • health checks
  • restart behaviour
  • exposed ports

Starting an application is then normally as simple as:

cd ~/docker/example
docker compose up -d

Checking it:

docker compose ps

Viewing its logs:

docker compose logs -f

Stopping it:

docker compose stop

These commands form the basic vocabulary for managing the entire server.


Part 4 — Create the Shared Docker Network

One of the most important decisions in this setup is to create a shared Docker network for services that need to communicate with one another.

Create it once:

docker network create proxy-net

Verify it:

docker network ls | grep proxy-net

The network is deliberately created outside the individual Compose projects.

Compose files therefore reference it as an external network:

networks:
  proxy-net:
    external: true

This is important.

The network is infrastructure shared by the applications rather than something owned by an individual application.

It also means that removing or recreating one application cannot accidentally destroy the network used by all the other services.


Part 5 — Why Container Names Matter

Once services are attached to proxy-net, they can communicate using their Docker container names.

For example:

nextcloud
collabora
mailrelay
vaultwarden
immich-kiosk

Nginx Proxy Manager can therefore forward traffic directly to:

nextcloud:80
collabora:9980
vaultwarden:80
mailrelay:25
immich-kiosk:3000

There is no need to discover the container's IP address.

Docker's internal DNS resolves the container name automatically.

This also means that application traffic can remain inside Docker rather than travelling through the Mac's host networking stack.


Part 6 — Persistent Storage

Containers themselves should generally be treated as disposable.

The important application data therefore needs to live outside the container.

For example:

volumes:
  - ./config:/var/www/html/config
  - ./data:/var/www/html/data

If the container is subsequently recreated, the data remains.

This distinction is fundamental:

Docker Container
      │
      ├── Application
      └── Temporary container filesystem
                │
                X
             disposable

Persistent Volume / Bind Mount
      │
      └── Configuration and data
                │
                ✓
             retained

For large data collections, I use the external storage attached to the Mac rather than consuming the Mac's internal SSD.

This is particularly useful for photography, documents and other large collections.


Part 7 — Keep Secrets Out of Compose Files

Passwords, API keys and SMTP credentials should not normally be written directly into a Compose file.

Instead, use an .env file where appropriate:

nano .env

For example:

RELAYHOST_USERNAME=your_brevo_login
RELAYHOST_PASSWORD=your_brevo_smtp_key

Protect the file:

chmod 600 .env

The .env file should also be excluded from public repositories and treated as sensitive data.

I additionally store important credentials and recovery information in Vaultwarden.


Part 8 — The Role of Nginx Proxy Manager

Once Docker is working, Nginx Proxy Manager becomes the gateway to the externally accessible services.

Rather than exposing every container directly:

Internet
   │
   ├── :80  → Nextcloud
   ├── :443 → Immich
   ├── :3001 → Uptime Kuma
   ├── :9980 → Collabora
   └── etc.

the preferred architecture is:

Internet
    │
    ▼
Router
    │
  :80/:443
    │
    ▼
Nginx Proxy Manager
    │
    ▼
proxy-net
    │
    ├── Nextcloud
    ├── Immich
    ├── Ghost
    ├── Vaultwarden
    └── other services

Only the gateway needs to be exposed to the internet.

Individual services can remain completely internal to Docker.

Nginx Proxy Manager also provides:

  • HTTPS termination
  • Let's Encrypt certificates
  • HTTP → HTTPS redirection
  • reverse proxying
  • WebSocket support
  • access restrictions
  • request filtering
  • a central place to manage external hostnames

The complete Nginx Proxy Manager installation and security configuration is covered separately in:

Making self-hosted services available and Secure under Docker


Part 9 — Starting the Services in the Correct Order

With multiple interconnected applications, startup order matters.

For example, Nextcloud depends on MariaDB and Redis.

Collabora depends on being able to communicate with Nextcloud.

The various applications may also depend on the shared proxy-net network.

Finally, Nginx Proxy Manager needs the upstream containers to exist and be attached to the network before it attempts to configure its proxies.

I therefore use a startup script which starts the services in a controlled order.

The basic principle is:

Docker Desktop
      │
      ▼
proxy-net
      │
      ▼
Supporting services
      │
      ├── MariaDB
      ├── Redis
      ├── Mail Relay
      └── other backends
      │
      ▼
Application containers
      │
      ├── Nextcloud
      ├── Immich
      ├── Ghost
      ├── Vaultwarden
      └── etc.
      │
      ▼
Nginx Proxy Manager

Nginx Proxy Manager should start last.

This avoids the common problem where NPM starts before one or more upstream containers have joined proxy-net, resulting in errors such as:

host not found in upstream

Part 10 — The Services

The following articles describe the individual services in detail.

Each title below will eventually link to its own installation, security and update guide.

Setting up a mail relay for self-hosted services under Docker

Postfix · Brevo · SMTP

A central SMTP relay for all the self-hosted applications.

Instead of configuring every application with its own external SMTP credentials, applications send mail internally to the Docker container at:

mailrelay:25

Postfix then authenticates with Brevo over encrypted SMTP and delivers the message to its destination.

This provides a simple centralised mail path for notifications from Ghost, Nextcloud, Vaultwarden, Paperless, Uptime Kuma and other applications.

The relay is deliberately not exposed to the internet.


Making self-hosted services available and Secure under Docker

Nginx Proxy Manager · Cloudflare DNS · Let's Encrypt · HTTPS

Nginx Proxy Manager is the front door to the Docker environment.

It maps public DNS names such as:

cloud.plainshawk.co.uk
photos.plainshawk.co.uk
blog.plainshawk.co.uk

to the appropriate internal Docker containers.

It also manages Let's Encrypt certificates and terminates HTTPS connections.

Security controls include restricting the NPM administration interface to the local network, enabling two-factor authentication, blocking common scanner traffic and restricting sensitive services to the home network where appropriate.


Monitoring the Docker Services

Uptime Kuma · Service monitoring · Availability alerts

Uptime Kuma provides a central dashboard showing whether the various services are available.

It can monitor HTTP/HTTPS endpoints, TCP ports and internal Docker services.

This provides an early warning when something fails rather than relying on discovering the problem when a service is needed.

Typical monitors include:

Nextcloud
Collabora
Immich
Ghost
Vaultwarden
Mail Relay
Nginx Proxy Manager

Setting up a Blog Server using Docker

Ghost · MySQL · Nginx Proxy Manager

Ghost provides the public-facing Plainshawk blog.

Running it in Docker keeps the blog application isolated from the rest of the server while persistent storage preserves the content and configuration.

Nginx Proxy Manager provides the public HTTPS endpoint, while the Ghost container itself remains inside the Docker environment.

The blog is available at:

https://blog.plainshawk.co.uk

Setting up an Office / Collaboration Space under Docker

Nextcloud · MariaDB · Redis · Collabora

Nextcloud provides the private cloud and collaboration platform.

It provides:

  • file storage
  • file synchronisation
  • sharing
  • calendars
  • contacts
  • collaborative applications
  • web access

MariaDB provides the database and Redis provides caching and transactional file locking.

Collabora Online adds browser-based editing for:

  • Word documents
  • spreadsheets
  • presentations

The result is effectively a small private office suite running entirely within the Docker environment.


Immich · PostgreSQL · Redis · Machine Learning

Immich provides the main personal photography platform.

It provides:

  • photo and video storage
  • automatic organisation
  • albums
  • searching
  • facial recognition
  • location information
  • mobile applications
  • sharing
  • automatic uploads

The large photo library is stored on persistent external storage while the application and supporting services run in Docker.

Nginx Proxy Manager provides the external HTTPS endpoint.


Adding a rotating picture display under Docker

Immich Kiosk · Immich API · Full-screen slideshow

Immich Kiosk provides a dedicated display for photographs already stored in Immich.

Rather than duplicating the photo library, the kiosk connects directly to Immich over the internal Docker network.

This makes it possible to put an old tablet, computer or Raspberry Pi connected to a display into full-screen slideshow mode.

The kiosk can display:

  • random photographs
  • albums
  • people
  • favourites
  • memories
  • different layouts
  • configurable slideshow transitions

It is particularly useful as a permanent digital photo frame.


Setting up a Password Store under Docker

Vaultwarden · Bitwarden-compatible password management

Vaultwarden provides a self-hosted password manager compatible with the Bitwarden ecosystem.

It stores passwords, secure notes, identities and other credentials in the private Docker environment.

It also provides an important role in managing the self-hosted infrastructure itself.

For example, I use it to store:

  • Docker service credentials
  • SMTP credentials
  • API keys
  • recovery codes
  • administrator passwords
  • other infrastructure secrets

Because Vaultwarden itself is a security-critical application, it receives additional access restrictions and should always be protected by strong authentication and HTTPS.


Setting up a Home Inventory under Docker

Homebox · Household inventory management

Homebox provides a searchable inventory of possessions around the home.

Items can be organised by:

  • location
  • category
  • manufacturer
  • model
  • serial number
  • purchase information
  • warranty information
  • notes

It is useful for maintaining an inventory of equipment, appliances, tools, electronics and other possessions.

Because the inventory is personal information, access is restricted to the home network.


Setting up a Book Library under Docker

Calibre · Calibre-Web · Digital library management

Calibre provides the underlying book library and ebook management environment.

Calibre-Web provides a convenient web interface for browsing and accessing the collection.

Together they provide:

  • ebook storage
  • metadata management
  • authors and series
  • searching
  • browsing
  • reading
  • downloading
  • library administration

The separation between the underlying Calibre library and the web interface also makes it possible to keep the library management environment relatively restricted.


Setting up a Document Store under Docker

Paperless-ngx · Document management · OCR

Paperless-ngx provides a searchable digital document archive.

Documents can be uploaded and automatically processed using OCR, making scanned documents searchable rather than simply storing them as images.

Documents can be organised using:

  • correspondents
  • document types
  • tags
  • dates
  • custom fields

This makes Paperless particularly useful for maintaining a digital archive of household correspondence, receipts, manuals, certificates and other paperwork.


Part 11 — Security Principles

Running services at home does not automatically make them secure.

The most important principle is:

Do not expose a service to the internet unless there is a reason for it to be there.

The Docker environment therefore follows several basic rules.

1. Minimise exposed ports

Where possible, containers communicate through proxy-net rather than exposing ports on the Mac.

2. Use HTTPS externally

Publicly accessible services use Nginx Proxy Manager and Let's Encrypt.

3. Restrict administration interfaces

Administration interfaces should not normally be exposed publicly.

Where possible, they are restricted to the home network.

4. Use strong authentication

Passwords should be unique and stored in Vaultwarden.

Two-factor authentication should be enabled wherever it is available.

5. Keep credentials out of Compose files

Secrets belong in protected .env files or a suitable secrets-management system.

6. Keep Docker images updated

Containers are software, and software contains vulnerabilities.

Updates should therefore be performed regularly.

7. Back up persistent data

A Docker container can be recreated.

Your data cannot necessarily be recreated.

Backups therefore cover databases, application configuration, certificates and important persistent storage.


Part 12 — Updating the Environment

One of the advantages of this arrangement is that applications can be updated independently.

A typical update looks like:

cd ~/docker/example
docker compose pull
docker compose up -d --force-recreate

However, not every application should be updated in exactly the same way.

Some applications have databases and require migrations.

Some require major versions to be installed sequentially.

Some have no persistent database and can simply be recreated.

The individual service articles therefore document the appropriate update procedure for each application.

A particularly important rule is:

Always read the application's release notes before performing a major version upgrade.

Never assume that changing latest or pulling a new image is sufficient.


Part 13 — Backups

A self-hosted server is only as good as its backups.

The backup strategy covers both application data and configuration.

Typical backup targets include:

Docker configuration
Application configuration
Databases
Photo library
Document library
Book library
Nextcloud data
Nginx Proxy Manager configuration
Let's Encrypt certificates

For databases, a database-aware backup is preferable to simply copying a live database directory.

For example, MariaDB can be backed up using:

docker exec nextcloud-db mariadb \
  -u nextcloud \
  -pPASSWORD \
  nextcloud > nextcloud_backup.sql

The individual service guides document the specific backup requirements for each application.


Part 14 — Troubleshooting Philosophy

Docker makes it very easy to create a complicated environment.

When something stops working, resist the temptation to immediately rebuild everything.

Work from the bottom upwards.

Is Docker running?

docker info

Is the container running?

docker ps

What does the container say?

docker logs <container>

Is it connected to the correct network?

docker inspect <container>

Can containers communicate?

For example:

docker exec <container> wget -qO- http://other-container:port/

Is Nginx Proxy Manager able to reach the upstream?

If the application works internally but returns a 502 externally, the problem is probably somewhere between NPM and the upstream container rather than with the application itself.

This layered approach makes troubleshooting much easier.


The Result

The finished environment is effectively a small private cloud running on a single Mac Mini:

                         INTERNET
                            │
                    Cloudflare DNS
                            │
                      Home Router
                       :80 / :443
                            │
                            ▼
                ┌──────────────────────┐
                │ Nginx Proxy Manager  │
                │  HTTPS / Let's Encrypt│
                └──────────┬───────────┘
                           │
                       proxy-net
                           │
       ┌───────────────────┼───────────────────┐
       │                   │                   │
       ▼                   ▼                   ▼
   Nextcloud             Immich              Ghost
   + Collabora         + Kiosk               Blog
       │                   │                   │
       ├───────────────┬───┴───────────────────┤
       │               │                       │
       ▼               ▼                       ▼
 Vaultwarden        Paperless               Homebox
 Passwords          Documents               Inventory
       │               │                       │
       ├───────────────┼───────────────────────┤
       ▼               ▼                       ▼
    Calibre         Uptime Kuma            Mail Relay
   Book Library      Monitoring          Postfix → Brevo

The individual applications perform very different jobs, but they share the same underlying infrastructure:

Docker provides isolation.

Docker Compose provides repeatable configuration.

Persistent volumes preserve the data.

proxy-net provides internal connectivity.

Nginx Proxy Manager provides the secure external gateway.

Cloudflare provides DNS.

Let's Encrypt provides certificates.

Uptime Kuma provides monitoring.

Backups provide recovery.

Together they turn a small Mac Mini into a surprisingly capable self-hosted server.