mirror of
https://github.com/tomasriveral/vps-config.git
synced 2026-08-12 10:30:45 +02:00
rewrite to debian
This commit is contained in:
@@ -1 +1,312 @@
|
|||||||
# vps-config
|
Uses debian 13 as I had difficulties setting up everything in nixos.
|
||||||
|
|
||||||
|
# Personal Server Setup Guide
|
||||||
|
|
||||||
|
This document explains how to rebuild and deploy the personal server stack on a fresh Debian VPS.
|
||||||
|
|
||||||
|
The server uses:
|
||||||
|
|
||||||
|
* Debian
|
||||||
|
* Docker CE
|
||||||
|
* Docker Compose
|
||||||
|
* Caddy as reverse proxy
|
||||||
|
* Containers for all self-hosted services
|
||||||
|
* Git as the source of truth for configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Create the VPS
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
|
||||||
|
* Debian 13
|
||||||
|
* SSH key authentication enabled
|
||||||
|
* Public IPv4 enabled
|
||||||
|
* Firewall enabled
|
||||||
|
|
||||||
|
After creation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Bootstrap the server
|
||||||
|
|
||||||
|
Copy the bootstrap script to the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp bootstrap.sh root@SERVER_IP:
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x bootstrap.sh
|
||||||
|
./bootstrap.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs:
|
||||||
|
|
||||||
|
* Docker CE
|
||||||
|
* Docker Compose
|
||||||
|
* Git
|
||||||
|
* Basic administration tools
|
||||||
|
* Firewall rules
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Create an administrator user
|
||||||
|
|
||||||
|
Do not run everything as root.
|
||||||
|
|
||||||
|
Create a user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adduser USERNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
Add permissions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
usermod -aG sudo,docker USERNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy SSH keys:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /home/USERNAME/.ssh
|
||||||
|
cp /root/.ssh/authorized_keys /home/USERNAME/.ssh/
|
||||||
|
chown -R USERNAME:USERNAME /home/USERNAME/.ssh
|
||||||
|
chmod 700 /home/USERNAME/.ssh
|
||||||
|
chmod 600 /home/USERNAME/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
Test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh USERNAME@SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Clone the server configuration
|
||||||
|
|
||||||
|
Create the deployment directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /opt/server
|
||||||
|
cd /opt/server
|
||||||
|
```
|
||||||
|
|
||||||
|
Clone the configuration repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone YOUR_REPOSITORY_URL .
|
||||||
|
```
|
||||||
|
|
||||||
|
The structure should look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
server/
|
||||||
|
├── compose.yml
|
||||||
|
├── Caddyfile
|
||||||
|
├── .env
|
||||||
|
├── scripts/
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Configure secrets
|
||||||
|
|
||||||
|
Never store passwords or tokens in Git.
|
||||||
|
|
||||||
|
Create:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```env
|
||||||
|
DOMAIN=example.com
|
||||||
|
|
||||||
|
FRESHRSS_ADMIN_PASSWORD=change_me
|
||||||
|
|
||||||
|
KDRIVE_USERNAME=user
|
||||||
|
KDRIVE_PASSWORD=password
|
||||||
|
```
|
||||||
|
|
||||||
|
Set permissions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod 600 .env
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Start the Docker stack
|
||||||
|
|
||||||
|
From `/opt/server`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Check running containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps
|
||||||
|
```
|
||||||
|
|
||||||
|
View logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Reverse proxy
|
||||||
|
|
||||||
|
Caddy handles HTTPS automatically.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
website.com
|
||||||
|
├── freshrss.website.com
|
||||||
|
├── archivebox.website.com
|
||||||
|
├── hledger.website.com
|
||||||
|
├── calendar.website.com
|
||||||
|
└── ntfy.website.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Caddyfile` defines where traffic goes.
|
||||||
|
|
||||||
|
After changing it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose restart caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Updating services
|
||||||
|
|
||||||
|
Update images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove unused images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker image prune
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Backups
|
||||||
|
|
||||||
|
Important data is stored in Docker volumes.
|
||||||
|
|
||||||
|
List volumes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker volume ls
|
||||||
|
```
|
||||||
|
|
||||||
|
Back up using:
|
||||||
|
|
||||||
|
* restic
|
||||||
|
* kDrive
|
||||||
|
* another storage provider
|
||||||
|
|
||||||
|
Recommended backup targets:
|
||||||
|
|
||||||
|
```
|
||||||
|
FreshRSS database
|
||||||
|
ArchiveBox data
|
||||||
|
Calendar/contact data
|
||||||
|
hledger files
|
||||||
|
Caddy configuration
|
||||||
|
.env secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Disaster recovery
|
||||||
|
|
||||||
|
To rebuild:
|
||||||
|
|
||||||
|
1. Install Debian
|
||||||
|
2. Run `bootstrap.sh`
|
||||||
|
3. Clone this repository
|
||||||
|
4. Restore backups
|
||||||
|
5. Restore `.env`
|
||||||
|
6. Start Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
The complete server should be operational again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Planned services
|
||||||
|
|
||||||
|
Current stack:
|
||||||
|
|
||||||
|
* Caddy
|
||||||
|
* FreshRSS
|
||||||
|
* ArchiveBox
|
||||||
|
* hledger-web
|
||||||
|
* Calendar/contact server
|
||||||
|
* ntfy
|
||||||
|
* Monitoring
|
||||||
|
|
||||||
|
Possible additions:
|
||||||
|
|
||||||
|
* Uptime Kuma
|
||||||
|
* Authelia or Authentik
|
||||||
|
* Vaultwarden
|
||||||
|
* Gitea
|
||||||
|
* Miniflux
|
||||||
|
* Mealie
|
||||||
|
* Paperless-ngx
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Principles
|
||||||
|
|
||||||
|
* Configuration belongs in Git
|
||||||
|
* Data belongs in backups
|
||||||
|
* Secrets never go into Git
|
||||||
|
* Containers are disposable
|
||||||
|
* Volumes contain persistent data
|
||||||
|
* The server should be reproducible
|
||||||
|
|||||||
Executable
+71
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "== Updating system =="
|
||||||
|
apt update
|
||||||
|
apt upgrade -y
|
||||||
|
|
||||||
|
echo "== Installing base packages =="
|
||||||
|
apt install -y \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
git \
|
||||||
|
vim \
|
||||||
|
htop \
|
||||||
|
unzip \
|
||||||
|
rsync \
|
||||||
|
jq \
|
||||||
|
ufw
|
||||||
|
|
||||||
|
echo "== Installing Docker repository key =="
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
|
||||||
|
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
||||||
|
-o /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
|
echo "== Adding Docker repository =="
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
|
||||||
|
https://download.docker.com/linux/debian \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||||
|
> /etc/apt/sources.list.d/docker.list
|
||||||
|
|
||||||
|
apt update
|
||||||
|
|
||||||
|
echo "== Installing Docker =="
|
||||||
|
apt install -y \
|
||||||
|
docker-ce \
|
||||||
|
docker-ce-cli \
|
||||||
|
containerd.io \
|
||||||
|
docker-buildx-plugin \
|
||||||
|
docker-compose-plugin
|
||||||
|
|
||||||
|
systemctl enable docker
|
||||||
|
systemctl start docker
|
||||||
|
|
||||||
|
echo "== Configuring firewall =="
|
||||||
|
ufw default deny incoming
|
||||||
|
ufw default allow outgoing
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
ufw allow 22/tcp
|
||||||
|
|
||||||
|
# Web
|
||||||
|
ufw allow 80/tcp
|
||||||
|
ufw allow 443/tcp
|
||||||
|
|
||||||
|
ufw --force enable
|
||||||
|
|
||||||
|
echo "== Enabling useful services =="
|
||||||
|
systemctl enable --now ssh
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "================================"
|
||||||
|
echo "Bootstrap finished!"
|
||||||
|
echo
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
echo "================================"
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{ pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
system.stateVersion = "26.05";
|
|
||||||
|
|
||||||
networking.hostName = "server";
|
|
||||||
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = true;
|
|
||||||
|
|
||||||
services.openssh = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
PasswordAuthentication = false;
|
|
||||||
PermitRootLogin = "prohibit-password";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
allowedTCPPorts = [
|
|
||||||
22
|
|
||||||
80
|
|
||||||
443
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
git
|
|
||||||
vim
|
|
||||||
curl
|
|
||||||
wget
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
disko.devices = {
|
|
||||||
disk = {
|
|
||||||
main = {
|
|
||||||
device = "/dev/sda";
|
|
||||||
|
|
||||||
type = "disk";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
|
|
||||||
partitions = {
|
|
||||||
ESP = {
|
|
||||||
size = "512M";
|
|
||||||
|
|
||||||
type = "EF00";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "vfat";
|
|
||||||
mountpoint = "/boot";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
root = {
|
|
||||||
size = "100%";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "ext4";
|
|
||||||
mountpoint = "/";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
description = "Hetzner NixOS vps";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
|
||||||
|
|
||||||
disko = {
|
|
||||||
url = "github:nix-community/disko";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
nixos-anywhere.url = "github:numtide/nixos-anywhere";
|
|
||||||
|
|
||||||
home-manager = {
|
|
||||||
url = "github:nix-community/home-manager/release-26.05";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, disko, home-manager, ... }:
|
|
||||||
{
|
|
||||||
nixosConfigurations.server = nixpkgs.lib.nixosSystem {
|
|
||||||
system = "x86_64-linux";
|
|
||||||
|
|
||||||
modules = [
|
|
||||||
disko.nixosModules.disko
|
|
||||||
./disko.nix
|
|
||||||
./configuration.nix
|
|
||||||
./users.nix
|
|
||||||
|
|
||||||
home-manager.nixosModules.home-manager
|
|
||||||
|
|
||||||
{
|
|
||||||
home-manager.users.root = import ./home.nix;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user