Docker Deployment
Docker is the recommended way to run Homepage in production.
Quick start
git clone https://codeberg.org/thelinuxcast/Homepage.git
cd Homepage
docker compose up -dThere 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
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-stoppedKey points:
./data:/app/data— persists the SQLite database, the credential encryption key and backups across container rebuilds- The credential encryption key is generated into
./dataon 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.sockinto the app container. Access to the Docker socket is equivalent to root on the host, and:rodoes 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
git pull
docker compose up -d --buildThe 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
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)
# 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