Document nginx vserver setup

This commit is contained in:
2026-04-26 19:41:21 +02:00
parent 26960a8a15
commit 61f11eed38
3 changed files with 117 additions and 18 deletions

View File

@@ -1,14 +1,18 @@
# SSH host or alias from ~/.ssh/config. # SSH host or alias from ~/.ssh/config.
REMOTE_HOST=myvserver # This can also be a literal user@host value, for example root@myvserver.
REMOTE_HOST=root@myvserver
# Remote filesystem layout. # Remote filesystem layout.
REMOTE_APP_DIR=/opt/rolemasterdb REMOTE_APP_DIR=/root/docker/rolemasterdb
REMOTE_DATA_DIR=/opt/rolemasterdb/data REMOTE_DATA_DIR=/root/docker/rolemasterdb/data
# Docker names on the remote host. # Docker names on the remote host.
IMAGE_NAME=rolemasterdb IMAGE_NAME=rolemasterdb
CONTAINER_NAME=rolemasterdb CONTAINER_NAME=rolemasterdb
# Bind the container only on loopback so nginx is the public entrypoint.
HOST_BIND_ADDRESS=127.0.0.1
# Port mapping: host:container. # Port mapping: host:container.
HOST_PORT=8080 HOST_PORT=8080
CONTAINER_PORT=8080 CONTAINER_PORT=8080

View File

@@ -1,12 +1,18 @@
# Docker Deployment To A Linux VServer # Docker Deployment To A Linux VServer
This repo now includes a deployment script for shipping `RolemasterDb.App` to a remote Linux server that you reach with: This repo now includes a deployment script for shipping `RolemasterDb.App` to a remote Linux server that you reach as `root`, for example with:
```bash ```bash
ssh myvserver ssh myvserver
``` ```
The script publishes the app locally, uploads a release bundle over `scp`, builds the Docker image on the server, and replaces the running container. The intended public URL is:
```text
https://rolemasterdb.franktovar.de
```
The script publishes the app locally, uploads a release bundle over `scp`, builds the Docker image on the server, and replaces the running container. Nginx remains a one-time manual setup on the host and proxies to the container over localhost.
## Files ## Files
@@ -19,10 +25,12 @@ The script publishes the app locally, uploads a release bundle over `scp`, build
On the vserver: On the vserver:
1. Install Docker. 1. Install Docker.
2. Make sure the SSH user can run `docker`, or set `REMOTE_USE_SUDO=1` in the deploy config. 2. Install nginx.
3. Open the host port you want to expose, for example `8080`. 3. Install a certificate tool such as `certbot` plus the nginx plugin if you want automated certificate provisioning.
4. Make sure `rolemasterdb.franktovar.de` resolves to this server. Your wildcard `A` and `AAAA` records already satisfy that.
The deploy script stores releases under `REMOTE_APP_DIR/releases/<timestamp>` and keeps the SQLite database in `REMOTE_DATA_DIR/rolemaster.db`. The deploy script stores releases under `REMOTE_APP_DIR/releases/<timestamp>` and keeps the SQLite database in `REMOTE_DATA_DIR/rolemaster.db`.
The container should stay behind nginx, so the default deploy config binds it to `127.0.0.1:8080`.
## Local Setup ## Local Setup
@@ -34,13 +42,78 @@ cp deploy/vserver.env.example deploy/vserver.env
Adjust the values in `deploy/vserver.env` as needed: Adjust the values in `deploy/vserver.env` as needed:
- `REMOTE_HOST`: your SSH host or alias, for example `myvserver` - `REMOTE_HOST`: your SSH host or alias, for example `root@myvserver`
- `REMOTE_APP_DIR`: base directory on the server, for example `/opt/rolemasterdb` - `REMOTE_APP_DIR`: base directory on the server, default `/root/docker/rolemasterdb`
- `REMOTE_DATA_DIR`: persistent directory for the SQLite database - `REMOTE_DATA_DIR`: persistent directory for the SQLite database
- `HOST_PORT`: public port on the vserver - `HOST_BIND_ADDRESS`: default `127.0.0.1` so only nginx can reach the container
- `REMOTE_USE_SUDO`: set to `1` if remote Docker commands need `sudo` - `HOST_PORT`: nginx upstream port on the host, default `8080`
- `REMOTE_USE_SUDO`: keep `0` when you deploy as `root`
- `REMOTE_ENV_FILE`: optional existing env file on the server to pass through to `docker run` - `REMOTE_ENV_FILE`: optional existing env file on the server to pass through to `docker run`
## One-Time Server Setup
Create the base directories on the server:
```bash
ssh myvserver
mkdir -p /root/docker/rolemasterdb/data
```
Deploy once so the container exists:
```bash
./scripts/deploy-vserver.sh
```
Create an nginx site config at `/etc/nginx/sites-available/rolemasterdb.franktovar.de`:
```nginx
server {
listen 80;
listen [::]:80;
server_name rolemasterdb.franktovar.de;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300;
}
}
```
Enable it and reload nginx:
```bash
ln -s /etc/nginx/sites-available/rolemasterdb.franktovar.de /etc/nginx/sites-enabled/rolemasterdb.franktovar.de
nginx -t
systemctl reload nginx
```
At this point `http://rolemasterdb.franktovar.de` should work.
## HTTPS Setup
If `certbot` and the nginx plugin are installed, request the certificate:
```bash
certbot --nginx -d rolemasterdb.franktovar.de
```
That should update the nginx config to serve HTTPS and normally also install an HTTP-to-HTTPS redirect. Verify the generated config and then reload nginx if needed:
```bash
nginx -t
systemctl reload nginx
```
After that, `https://rolemasterdb.franktovar.de` should terminate TLS in nginx and proxy traffic to `127.0.0.1:8080`.
## Deploy ## Deploy
Run: Run:
@@ -61,6 +134,7 @@ Or point at a different config file:
2. Precreates `publish/wwwroot/components/layout` and `publish/wwwroot/components/shared` before publish. 2. Precreates `publish/wwwroot/components/layout` and `publish/wwwroot/components/shared` before publish.
This is required because the current project otherwise fails to publish two JavaScript static assets. This is required because the current project otherwise fails to publish two JavaScript static assets.
3. Bundles the publish output, `src/RolemasterDb.App/rolemaster.db`, and the runtime Dockerfile into `artifacts/deploy/<release-id>/`. 3. Bundles the publish output, `src/RolemasterDb.App/rolemaster.db`, and the runtime Dockerfile into `artifacts/deploy/<release-id>/`.
The publish output already includes `src/RolemasterDb.App/import-artifacts`, so those files are shipped as part of every release bundle.
4. Uploads the bundle to the vserver. 4. Uploads the bundle to the vserver.
5. Extracts the bundle on the vserver. 5. Extracts the bundle on the vserver.
6. Seeds `REMOTE_DATA_DIR/rolemaster.db` on first deploy only. 6. Seeds `REMOTE_DATA_DIR/rolemaster.db` on first deploy only.
@@ -69,7 +143,7 @@ Or point at a different config file:
```text ```text
--restart unless-stopped --restart unless-stopped
-p HOST_PORT:CONTAINER_PORT -p HOST_BIND_ADDRESS:HOST_PORT:CONTAINER_PORT
-v REMOTE_DATA_DIR:/app/data -v REMOTE_DATA_DIR:/app/data
ConnectionStrings__RolemasterDb=Data Source=/app/data/rolemaster.db ConnectionStrings__RolemasterDb=Data Source=/app/data/rolemaster.db
``` ```
@@ -81,7 +155,9 @@ Running the same deploy command again will:
- publish a fresh release bundle - publish a fresh release bundle
- upload a new timestamped release - upload a new timestamped release
- keep the existing database in `REMOTE_DATA_DIR` - keep the existing database in `REMOTE_DATA_DIR`
- keep the imported artifact files inside the image for that release
- rebuild the image and restart the container - rebuild the image and restart the container
- leave the nginx config and TLS setup untouched
## Useful Remote Commands ## Useful Remote Commands
@@ -96,3 +172,10 @@ Follow logs:
```bash ```bash
ssh myvserver docker logs -f rolemasterdb ssh myvserver docker logs -f rolemasterdb
``` ```
Check nginx:
```bash
ssh myvserver nginx -t
ssh myvserver systemctl status nginx
```

View File

@@ -26,6 +26,7 @@ REMOTE_APP_DIR="${REMOTE_APP_DIR:-/opt/rolemasterdb}"
REMOTE_DATA_DIR="${REMOTE_DATA_DIR:-$REMOTE_APP_DIR/data}" REMOTE_DATA_DIR="${REMOTE_DATA_DIR:-$REMOTE_APP_DIR/data}"
IMAGE_NAME="${IMAGE_NAME:-rolemasterdb}" IMAGE_NAME="${IMAGE_NAME:-rolemasterdb}"
CONTAINER_NAME="${CONTAINER_NAME:-rolemasterdb}" CONTAINER_NAME="${CONTAINER_NAME:-rolemasterdb}"
HOST_BIND_ADDRESS="${HOST_BIND_ADDRESS:-}"
HOST_PORT="${HOST_PORT:-8080}" HOST_PORT="${HOST_PORT:-8080}"
CONTAINER_PORT="${CONTAINER_PORT:-8080}" CONTAINER_PORT="${CONTAINER_PORT:-8080}"
REMOTE_USE_SUDO="${REMOTE_USE_SUDO:-0}" REMOTE_USE_SUDO="${REMOTE_USE_SUDO:-0}"
@@ -89,6 +90,7 @@ ssh "$REMOTE_HOST" bash -s -- \
"$REMOTE_DATA_DIR" \ "$REMOTE_DATA_DIR" \
"$IMAGE_NAME" \ "$IMAGE_NAME" \
"$CONTAINER_NAME" \ "$CONTAINER_NAME" \
"$HOST_BIND_ADDRESS" \
"$HOST_PORT" \ "$HOST_PORT" \
"$CONTAINER_PORT" \ "$CONTAINER_PORT" \
"$REMOTE_USE_SUDO" \ "$REMOTE_USE_SUDO" \
@@ -100,10 +102,11 @@ remote_app_dir="$2"
remote_data_dir="$3" remote_data_dir="$3"
image_name="$4" image_name="$4"
container_name="$5" container_name="$5"
host_port="$6" host_bind_address="$6"
container_port="$7" host_port="$7"
remote_use_sudo="$8" container_port="$8"
remote_env_file="$9" remote_use_sudo="$9"
remote_env_file="${10}"
release_dir="$remote_app_dir/releases/$release_id" release_dir="$remote_app_dir/releases/$release_id"
tarball_path="$release_dir/release.tar.gz" tarball_path="$release_dir/release.tar.gz"
@@ -132,13 +135,18 @@ docker_run_args=(
-d -d
--name "$container_name" --name "$container_name"
--restart unless-stopped --restart unless-stopped
-p "$host_port:$container_port"
-e "ASPNETCORE_ENVIRONMENT=Production" -e "ASPNETCORE_ENVIRONMENT=Production"
-e "ASPNETCORE_URLS=http://+:$container_port" -e "ASPNETCORE_URLS=http://+:$container_port"
-e "ConnectionStrings__RolemasterDb=Data Source=/app/data/rolemaster.db" -e "ConnectionStrings__RolemasterDb=Data Source=/app/data/rolemaster.db"
-v "$remote_data_dir:/app/data" -v "$remote_data_dir:/app/data"
) )
if [[ -n "$host_bind_address" ]]; then
docker_run_args+=(-p "$host_bind_address:$host_port:$container_port")
else
docker_run_args+=(-p "$host_port:$container_port")
fi
if [[ -n "$remote_env_file" ]]; then if [[ -n "$remote_env_file" ]]; then
docker_run_args+=(--env-file "$remote_env_file") docker_run_args+=(--env-file "$remote_env_file")
fi fi
@@ -152,7 +160,11 @@ ln -sfn "$release_dir" "$remote_app_dir/current"
echo "Deployment complete." echo "Deployment complete."
echo "Release: $release_id" echo "Release: $release_id"
echo "Container: $container_name" echo "Container: $container_name"
echo "Host port: $host_port" if [[ -n "$host_bind_address" ]]; then
echo "Published at: $host_bind_address:$host_port -> $container_port"
else
echo "Published at: $host_port -> $container_port"
fi
EOF EOF
echo "Local bundle ready at $tarball_path" echo "Local bundle ready at $tarball_path"