Skip to content

Docker Deployment

Docker is the recommended way to run Homepage in production.

Quick start

bash
git clone https://codeberg.org/thelinuxcast/Homepage.git
cd Homepage
docker compose up -d

There is no configuration file to create. Settings live inline in docker-compose.yml with comments explaining each one, and widget credentials are entered in the UI and stored encrypted.

The app will be available at http://localhost:3001.

Note: if you have other containers running, you may need to change the port

docker-compose.yml

The included docker-compose.yml builds the image locally and mounts the data directory:

Note: The included compose file has a section for nginx, this can be deleted. It is for hosting the docs and you don't need that

yaml
services:
  homepage:
    build: .
    ports:
      - "3001:3001"
    volumes:
      - ./data:/app/data
    environment:
      - DOCKER_HOST=tcp://docker-socket-proxy:2375
    depends_on:
      - docker-socket-proxy
    restart: unless-stopped

  # Only needed for the Docker widget.
  docker-socket-proxy:
    image: tecnativa/docker-socket-proxy:0.3.0
    environment:
      - CONTAINERS=1
      - POST=1
      - INFO=1
      - IMAGES=0
      - VOLUMES=0
      - NETWORKS=0
      - EXEC=0
      - BUILD=0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped

Key points:

  • ./data:/app/data — persists the SQLite database, the credential encryption key and backups across container rebuilds
  • The credential encryption key is generated into ./data on first run — no configuration needed. Back it up with the database; losing it means re-entering every widget credential. To keep the key outside the data directory, see the encryption key.
  • Do not mount /var/run/docker.sock into the app container. Access to the Docker socket is equivalent to root on the host, and :ro does not change that — it applies to the filesystem entry, not to the commands the daemon accepts. The proxy above exposes only container listing and start/stop/restart, which is all the Docker widget uses. Omit both if you do not need the widget.
  • restart: unless-stopped — auto-restarts on system reboot

Updating

bash
git pull
docker compose up -d --build

The database is preserved because it lives in the mounted data/ volume.

Schema migrations run automatically at startup and are logged. Upgrading from a version before credential encryption will, on first boot:

  • move the JWT secret and password hash out of the settings table, replacing the secret — everyone signs in again once, which is deliberate, because the old one was readable through the API;
  • encrypt widget credentials into their own table and compact the database so the superseded plaintext pages are actually released.

Take a copy of data/ before upgrading, as you would for any migration.

Running behind a reverse proxy

Caddy

homepage.example.com {
    reverse_proxy localhost:3001
}

nginx

nginx
server {
    listen 80;
    server_name homepage.example.com;

    location / {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # Rate limiting reads the client address from these. Without them every
        # request buckets under the proxy's own IP, so one caller tripping the
        # login limiter locks everyone out.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Manual build (no Docker)

bash
# Install all dependencies
npm install
cd client && npm install && cd ..
cd server && npm install && cd ..

# Build
cd client && npm run build && cd ..
cd server && npm run build && cd ..

# Copy built client into server's public directory
cp -r client/dist/* server/public/

# Start
cd server && npm start

Personal Homepage Dashboard