Self-Hosting Tools with docker-compose in 10 Minutes

Eyeglasses reflecting computer code on a monitor, ideal for technology and programming themes.
Eyeglasses reflecting computer code on a monitor, ideal for technology and programming themes.

Self-Hosting Tools with docker-compose in 10 Minutes

The digital world often pushes us towards managed services and cloud subscriptions. While convenient, this often means sacrificing privacy, control, and the opportunity to truly understand the underlying technology. Self-hosting offers a compelling alternative: running your own applications on your own hardware, be it a powerful server, a humble Raspberry Pi, or even your daily driver machine for testing.

The idea of self-hosting might conjure images of complex server setups, intricate network configurations, and endless debugging. Historically, that was often true. But thanks to technologies like Docker and especially docker-compose, that barrier has been dramatically lowered. This guide aims to show you how to get a functional, self-hosted application up and running in roughly 10 minutes, leveraging the power and simplicity of docker-compose.

Why Self-Host?

Before we dive into the “how,” let’s briefly touch on the “why”:

  • Control & Ownership: You own your data, your configuration, and your uptime. You’re not at the mercy of a third-party service’s policies, pricing changes, or sudden discontinuation.
  • Privacy: Reduce your reliance on companies that monetize your data. Keep your personal information and activities private.
  • Cost Savings: For many tools, especially those with free and open-source alternatives, self-hosting can be significantly cheaper than recurring subscription fees, especially over the long term.
  • Learning Opportunity: Setting up and maintaining your own services is an invaluable learning experience, deepening your understanding of networking, Linux, system administration, and software architecture.
  • Customization: Tailor applications precisely to your needs, integrating them with other self-hosted services in ways that managed solutions rarely allow.

Why docker-compose?

Docker revolutionized containerization, allowing applications and their dependencies to be bundled into isolated, portable units. While fantastic for single services, most real-world applications consist of multiple interconnected components (e.g., a web app, a database, a cache). This is where docker-compose shines.

docker-compose is a tool for defining and running multi-container Docker applications. With a single YAML file, you can configure all your services, networks, and volumes, then spin up the entire stack with a single command. Its key advantages include:

  • Simplicity: Define your entire application stack in a human-readable docker-compose.yml file. No more complex docker run commands for each container.
  • Portability: Your docker-compose.yml file can be shared and run on any system with Docker and docker-compose installed, ensuring consistent environments.
  • Networking: docker-compose automatically creates a default network, allowing services to communicate with each other using their service names (e.g., a web app can connect to a database service named db simply by using db as the hostname).
  • Orchestration: Easily start, stop, rebuild, and scale services defined in your docker-compose.yml.

Prerequisites (The “Real” 10 Minutes Starts Here)

While the goal is 10 minutes for spinning up a tool, you’ll need Docker and Docker Compose installed first. If you don’t have them, add a few minutes to your clock for installation.

  1. Operating System: Linux (Ubuntu, Debian, Fedora, etc.) is generally preferred for self-hosting due to its stability, resource efficiency, and community support. macOS and Windows users can use Docker Desktop which bundles Docker Engine and Compose.
  2. Docker Engine & Docker Compose:
    • Linux: Follow the official Docker Engine installation guide for your specific distribution: Install Docker Engine.
      • Then install Docker Compose. For newer Docker versions (20.10.0+), docker-compose is often included as docker compose (note the space, no hyphen). If not, or for older versions, install the standalone docker-compose plugin: Install Docker Compose.
    • macOS/Windows: Install Docker Desktop. It comes with both Docker Engine and docker-compose (as docker compose).
  3. Basic Command-Line Familiarity: You’ll be using cd, mkdir, and running commands like docker-compose up.

Note: The “10 minutes” is a target for the active setup process once prerequisites are met. Installation of Docker itself can take 5-15 minutes depending on your internet speed and system.


The 10-Minute Walkthrough: Self-Hosting Uptime Kuma

For this demonstration, we’ll self-host Uptime Kuma, an open-source, self-hosted monitoring tool that’s incredibly easy to set up and provides immediate visual feedback. It’s a fantastic way to monitor the uptime of your websites, APIs, and other services.

Step 1: Choose Your Tool(s) (Done: Uptime Kuma)

Uptime Kuma is a great choice because it’s lightweight and doesn’t require an external database for basic operation (it uses an embedded SQLite database by default). This simplifies the docker-compose.yml.

Step 2: Create a Project Directory

It’s good practice to create a dedicated directory for each docker-compose project. This helps keep configurations organized.

mkdir uptime-kuma
cd uptime-kuma

(Time: ~10 seconds)

Step 3: Create docker-compose.yml

This is the core of our setup. We’ll create a file named docker-compose.yml (or docker-compose.yaml) in our uptime-kuma directory.

# On Linux/macOS
nano docker-compose.yml
# Or if you prefer VS Code or another editor
code docker-compose.yml

Now, paste the following content into the file:

version: '3.8' # Specifies the Docker Compose file format version

services:
  uptime-kuma: # Defines a service named 'uptime-kuma'
    image: louislam/uptime-kuma:1 # The Docker image to use. '1' is the latest stable.
    container_name: uptime-kuma # A friendly name for the container
    volumes:
      - ./uptime-kuma-data:/app/data # Mounts a local directory for persistent data
    ports:
      - "3001:3001" # Maps host port 3001 to container port 3001
    restart: unless-stopped # Automatically restart the container unless manually stopped

Let’s break down this simple file:

  • version: '3.8': Specifies the Compose file format version. Always use the latest stable version (currently 3.8 or 3.9).
  • services:: This section defines the different services (containers) that make up your application. Here, we have just one: uptime-kuma.
  • image: louislam/uptime-kuma:1: Tells Docker Compose which image to pull from Docker Hub. louislam/uptime-kuma is the repository, and :1 specifies the tag (version).
  • container_name: uptime-kuma: Assigns a specific name to the Docker container, making it easier to identify.
  • volumes:: This is crucial for data persistence.
    • - ./uptime-kuma-data:/app/data maps a local directory named uptime-kuma-data (relative to your docker-compose.yml file) to the /app/data directory inside the container. This means any data Uptime Kuma stores will be saved on your host machine, surviving container restarts or even recreation. If you don’t use volumes, your data will be lost when the container is removed.
  • ports:: This exposes the container’s internal ports to your host machine.
    • - "3001:3001" maps host port 3001 to container port 3001. You will access Uptime Kuma via http://your_server_ip:3001.
  • restart: unless-stopped: This ensures that if the container crashes or your server reboots, Docker will automatically try to restart the uptime-kuma container.

Save and close the file.

(Time: ~3 minutes for typing/pasting and understanding)

Step 4: Spin it Up!

Now for the magic command. Make sure you are in the uptime-kuma directory where your docker-compose.yml file is located.

docker-compose up -d
  • up: Builds, creates, starts, and attaches to containers for a service.
  • -d: (detached mode) Runs the containers in the background, freeing up your terminal.

The first time you run this, Docker will download the louislam/uptime-kuma:1 image. This might take a minute or two depending on your internet connection. Subsequent runs will be much faster as the image will be cached locally.

You should see output similar to this:

[+] Running 1/1
 ⠿ Container uptime-kuma  Started

(Time: ~1-3 minutes, mostly download time)

Step 5: Verify and Access

Open your web browser and navigate to http://localhost:3001 (if running on your local machine) or http://YOUR_SERVER_IP:3001 (if running on a remote server).

You should be greeted by the Uptime Kuma setup screen. Create your admin user and password, and you’re ready to start monitoring!

To check the logs of your running container (useful for troubleshooting):

docker-compose logs -f uptime-kuma

Press Ctrl+C to exit the log stream.

(Time: ~1 minute)

Congratulations! You’ve successfully self-hosted Uptime Kuma with docker-compose in a matter of minutes.

Step 6: Stop and Remove (Optional but Good to Know)

When you’re done with a service or want to completely remove it, use:

docker-compose down

This command stops and removes the containers, networks, and volumes defined in your docker-compose.yml file. Important: The uptime-kuma-data directory on your host will remain, preserving your data even after docker-compose down. If you want to remove the data as well, you’ll need to manually delete that directory.

(Time: ~10 seconds)


More Advanced Concepts (Beyond 10 Minutes but Essential)

The 10-minute setup is just the beginning. For a robust self-hosting setup, consider these concepts:

1. Persistence with Volumes (Already Covered, But Re-emphasized)

Always, always, always use volumes for any data that needs to persist. Without them, your application’s data is ephemeral and tied to the container, meaning it will be lost if the container is removed or rebuilt.

2. Environment Variables

Many applications are configured via environment variables. Instead of modifying configuration files inside containers (which is not recommended), you pass variables via docker-compose.yml:

services:
  my-app:
    image: my-app-image
    environment:
      - DATABASE_HOST=db
      - DATABASE_PORT=5432
      - APP_DEBUG=false

3. Networking for Multi-Service Applications

When you have multiple services (e.g., a web app and a database), docker-compose automatically creates a network for them. Services can communicate using their service names as hostnames.

Example for a web app connecting to a PostgreSQL database:

version: '3.8'

services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb # 'db' is the service name

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - ./pgdata:/var/lib/postgresql/data

Here, the web service can access the db service simply by using db as the hostname.

4. Reverse Proxies & SSL (Crucial for Production)

Exposing services directly on ports like 3001 (http://ip:3001) isn’t ideal for production. A reverse proxy sits in front of your applications, handling:

  • SSL/TLS (HTTPS): Encrypting traffic with free certificates from Let’s Encrypt.
  • Domain Routing: Allowing you to access app1.yourdomain.com and app2.yourdomain.com instead of your_ip:port1 and your_ip:port2.
  • Centralized Logging/Security: A single point for managing access and logging.

Popular docker-compose friendly reverse proxies:

  • Nginx Proxy Manager (NPM): A fantastic graphical interface for managing Nginx proxy hosts, SSL certificates (via Let’s Encrypt), and basic authentication. Highly recommended for beginners. GitHub Link
  • Caddy: An automatic HTTPS server that’s incredibly simple to configure. Official Site
  • Traefik: A powerful and dynamic reverse proxy and load balancer, especially good for more complex setups with service discovery. Official Site

You would typically set up one of these in its own docker-compose.yml file, then point your domain to your server’s IP, and configure the proxy to forward requests to your internal docker-compose services (e.g., uptime-kuma on its internal network port).

5. Backups

Your data is paramount. Regularly back up your volumes directories to an external drive or cloud storage. For databases, consider using docker exec to run database-specific backup commands.

6. Updates

To update a Dockerized application to a newer version:

  1. Pull the new image:
    docker-compose pull
  2. Recreate and start containers:
    docker-compose up -d
    docker-compose will detect the new image and replace the old container while preserving your data (thanks to volumes!). Note: Always check the release notes of the application for breaking changes before updating.

7. Security Considerations

  • Firewall: Configure your server’s firewall (e.g., ufw on Linux) to only allow necessary incoming connections (e.g., SSH, HTTP/S, and perhaps ports for specific exposed services if not using a reverse proxy).
  • Strong Passwords: Use unique, strong passwords for all admin interfaces.
  • Least Privilege: Don’t run containers as root if not necessary (though many official images default to root for simplicity).
  • Regular Updates: Keep your host OS, Docker, and container images updated.

Common Pitfalls & Troubleshooting

  • Port Conflicts: “Error starting userland proxy: listen tcp 0.0.0.0:xxxx: bind: address already in use.” This means another process (or another container) is already using the port you’re trying to map. Change the host port in ports: "NEW_PORT:CONTAINER_PORT".
  • Incorrect Volume Paths: If your data isn’t persisting, double-check the host path in your volumes definition. Ensure it’s correct and has the necessary permissions.
  • YAML Syntax Errors: YAML is whitespace-sensitive. Even a single incorrect indentation can break the file. Use a YAML linter if you encounter ERROR: yaml.scanner.ScannerError.
  • Container Not Starting: Use docker-compose logs -f <service_name> to see why a container is failing to start. This is your primary debugging tool.
  • Firewall Blocking Access: If you can’t reach your application from another machine, check your server’s firewall rules.

Recommended Self-Hosted Tools (Beyond Uptime Kuma)

Once you’ve mastered the basics, a world of open-source tools awaits! Here are some popular ones, often with official docker-compose examples:

  • Portainer: A powerful web UI for managing Docker containers, images, volumes, and networks. Essential for visualizing your Docker environment. Installation Guide with Docker Compose
  • Homepage: A modern, customizable dashboard for all your self-hosted services. GitHub
  • Nginx Proxy Manager: As mentioned, a user-friendly reverse proxy with a web UI. GitHub
  • Vaultwarden: An open-source Bitwarden-compatible password manager. Secure and private. GitHub
  • Jellyfin: A free software media system that puts you in control of your media. An open-source alternative to Plex or Emby. Website
  • Nextcloud: A complete self-hosted cloud platform (file sync, calendar, contacts, photos, etc.), a great alternative to Google Drive or Dropbox. Website
  • Gitea: A lightweight and easy-to-set-up Git service. Ideal for hosting your own code repositories. Website
  • Immich: A self-hosted photo and video backup solution, similar to Google Photos. GitHub

Most of these tools provide sample docker-compose.yml files in their documentation or GitHub repositories, making setup a breeze.

Conclusion

Self-hosting with docker-compose isn’t just about saving money or gaining privacy; it’s about empowering yourself with knowledge and control over your digital life. The 10-minute promise might seem ambitious, but as you’ve seen, getting a basic service running is incredibly quick and accessible.

While this guide covers the initial sprint, the journey of self-hosting is an ongoing adventure of learning, optimization, and discovery. Start small, experiment, and don’t be afraid to break things (that’s why we have volumes for data persistence!). The satisfaction of running your own services is immense.

What will you self-host next? Share your journey in the comments below!

Last updated on