From 8582daf4c0eeec30b23768d1cf57ba2a002aeb82 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Tue, 7 Apr 2026 22:51:51 +0200 Subject: [PATCH 1/9] feat: systemd service file --- systemd/karl.service | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 systemd/karl.service diff --git a/systemd/karl.service b/systemd/karl.service new file mode 100644 index 0000000..9767b65 --- /dev/null +++ b/systemd/karl.service @@ -0,0 +1,34 @@ +[Unit] +Description=Karl +After=network.target +Wants=network.target +StartLimitBurst=3 +StartLimitIntervalSec=60 + +[Service] +User=karl +WorkingDirectory=/home/karl/app +ExecStart=/home/karl/app/venv/bin/gunicorn \ + -c /home/karl/app/gunicorn.conf.py \ + app.main:app + +# Graceful reload (sends SIGHUP) +ExecReload=/bin/kill -s HUP $MAINPID + +# Restart on failure +Restart=on-failure +RestartSec=5s + +# Security hardening +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ReadWritePaths=/var/log/karl /tmp + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=karl + +[Install] +WantedBy=multi-user.target From 3b464983ce66a4137aed130b7acfd7c561c04003 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Tue, 7 Apr 2026 23:00:24 +0200 Subject: [PATCH 2/9] feat: gunicorn config --- systemd/gunicorn.conf.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 systemd/gunicorn.conf.py diff --git a/systemd/gunicorn.conf.py b/systemd/gunicorn.conf.py new file mode 100644 index 0000000..3cdca6b --- /dev/null +++ b/systemd/gunicorn.conf.py @@ -0,0 +1,29 @@ +# Gunicorn configuration for FastAPI +import multiprocessing + +# Server socket +bind = "127.0.0.1:8000" +backlog = 2048 + +# Worker processes +# Rule of thumb: (2 * CPU cores) + 1 +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "uvicorn.workers.UvicornWorker" +worker_connections = 1000 +timeout = 30 +keepalive = 2 + +# Logging +accesslog = "/var/log/karl/access.log" +errorlog = "/var/log/karl/error.log" +loglevel = "info" +access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' + +# Process naming +proc_name = "karl" + +# Graceful shutdown +graceful_timeout = 30 + +# Preload app for faster worker spawning +preload_app = True From ef6f58b4e51e84f5766512788e6b7c991f5e56c4 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 00:14:42 +0200 Subject: [PATCH 3/9] feat: install script --- systemd/install.sh | 105 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 systemd/install.sh diff --git a/systemd/install.sh b/systemd/install.sh new file mode 100644 index 0000000..23479ee --- /dev/null +++ b/systemd/install.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# --- Kolory do logowania --- +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +log() { echo -e "${GREEN}[INFO]${NC} $1"; } +err() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; } + +# --- Sprawdzenie uprawnień roota --- +if [[ $EUID -ne 0 ]]; then + err "Ten skrypt wymaga uprawnień roota. Uruchom z sudo." +fi + +# --- 1. Wykrycie menedżera pakietów (apt / dnf) --- +if command -v apt-get &>/dev/null; then + PKG_MGR="apt-get" + PKG_UPDATE="apt-get update -y" + PKG_INSTALL="apt-get install -y" +elif command -v dnf &>/dev/null; then + PKG_MGR="dnf" + PKG_UPDATE="dnf check-update || true" + PKG_INSTALL="dnf install -y" +else + err "Nie znaleziono obsługiwanego menedżera pakietów (apt/dnf)." +fi + +log "Wykryto menedżer pakietów: $PKG_MGR" +$PKG_UPDATE + +# --- 2. Instalacja python3, curl, unzip --- +log "Instalacja python3, curl, unzip..." +$PKG_INSTALL python3 python3-pip curl unzip +$PKG_INSTALL python3-venv || true + +# --- 3. Instalacja Dockera (jeśli nie zainstalowany) --- +if command -v docker &>/dev/null; then + log "Docker już zainstalowany — pomijam." +else + log "Instalacja Dockera..." + curl -fsSL https://get.docker.com | bash + systemctl enable --now docker + log "Docker zainstalowany i uruchomiony." +fi + +# --- 4. Utworzenie użytkownika 'karl' --- +if id "karl" &>/dev/null; then + log "Użytkownik 'karl' już istnieje — pomijam." +else + log "Tworzenie użytkownika 'karl'..." + useradd -m -s /bin/bash karl +fi + +# --- 5. Dodanie 'karl' do grupy 'docker' --- +log "Dodawanie użytkownika 'karl' do grupy 'docker'..." +usermod -aG docker karl + +# --- 6. Pobranie i rozpakowanie archiwum --- +APP_URL="${1:?Podaj URL archiwum ZIP jako pierwszy argument}" +APP_DIR="/opt/karl/app" + +log "Pobieranie archiwum z: $APP_URL" +mkdir -p "$APP_DIR" +TMPZIP=$(mktemp /tmp/karl-app-XXXXXX.zip) +curl -fSL -o "$TMPZIP" "$APP_URL" +unzip -o "$TMPZIP" -d "$APP_DIR" +rm -f "$TMPZIP" +chown -R karl:karl "$APP_DIR" +log "Aplikacja rozpakowana do $APP_DIR" + +# --- 7. Sprawdzenie / instalacja uv --- +if command -v uv &>/dev/null; then + log "uv już zainstalowane." +else + log "Instalacja uv..." + pip3 install uv +fi + +# --- 8. Synchronizacja zależności (uv sync) --- +log "Uruchamianie 'uv sync' w katalogu aplikacji..." +cd "$APP_DIR" +sudo -u karl uv sync + +# --- 9. Kopiowanie pliku usługi systemd --- +SERVICE_SRC="$(dirname "$(realpath "$0")")/karl.service" +SERVICE_DST="/etc/systemd/system/karl.service" + +if [[ ! -f "$SERVICE_SRC" ]]; then + err "Nie znaleziono pliku $SERVICE_SRC" +fi + +log "Kopiowanie $SERVICE_SRC -> $SERVICE_DST" +cp "$SERVICE_SRC" "$SERVICE_DST" +chmod 644 "$SERVICE_DST" + +# --- 10. Przeładowanie systemctl i uruchomienie usługi --- +log "Przeładowanie systemd i włączanie usługi karl..." +systemctl daemon-reload +systemctl enable karl.service +systemctl start karl.service + +log "Instalacja zakończona pomyślnie! ✅" From e30218354dd8bf13af1d0fa357022968fd99b762 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 23:05:33 +0200 Subject: [PATCH 4/9] fix: app update --- systemd/install.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/systemd/install.sh b/systemd/install.sh index 23479ee..6749d1b 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -60,8 +60,29 @@ usermod -aG docker karl # --- 6. Pobranie i rozpakowanie archiwum --- APP_URL="${1:?Podaj URL archiwum ZIP jako pierwszy argument}" -APP_DIR="/opt/karl/app" +APP_DIR="/home/karl/app" +# --- Backup i czyszczenie istniejącego katalogu --- +if [[ -d "$APP_DIR" ]] && [[ -n "$(ls -A "$APP_DIR" 2>/dev/null)" ]]; then + log "Katalog $APP_DIR istnieje i nie jest pusty — tworzenie backupu..." + CONFIG_DIR="$APP_DIR/config" + if [[ -d "$CONFIG_DIR" ]]; then + BACKUP_DIR="/home/karl/backups" + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_PATH="$BACKUP_DIR/config_backup_$TIMESTAMP" + mkdir -p "$BACKUP_DIR" + cp -r "$CONFIG_DIR" "$BACKUP_PATH" + chown -R karl:karl "$BACKUP_DIR" + log "Backup utworzony: $BACKUP_PATH" + else + log "Katalog config nie istnieje — pomijam backup." + fi + log "Usuwanie katalogu $APP_DIR..." + rm -rf "$APP_DIR" + log "Katalog $APP_DIR usunięty." +fi + +# --- Instalacja / aktualizacja --- log "Pobieranie archiwum z: $APP_URL" mkdir -p "$APP_DIR" TMPZIP=$(mktemp /tmp/karl-app-XXXXXX.zip) From 532b32b5c73aeabe7a9b5621821f919ece6189bb Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 23:10:36 +0200 Subject: [PATCH 5/9] fix: venv --- systemd/install.sh | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/systemd/install.sh b/systemd/install.sh index 6749d1b..0bf7837 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -62,7 +62,7 @@ usermod -aG docker karl APP_URL="${1:?Podaj URL archiwum ZIP jako pierwszy argument}" APP_DIR="/home/karl/app" -# --- Backup i czyszczenie istniejącego katalogu --- +# --- 7. Backup i czyszczenie istniejącego katalogu --- if [[ -d "$APP_DIR" ]] && [[ -n "$(ls -A "$APP_DIR" 2>/dev/null)" ]]; then log "Katalog $APP_DIR istnieje i nie jest pusty — tworzenie backupu..." CONFIG_DIR="$APP_DIR/config" @@ -82,7 +82,7 @@ if [[ -d "$APP_DIR" ]] && [[ -n "$(ls -A "$APP_DIR" 2>/dev/null)" ]]; then log "Katalog $APP_DIR usunięty." fi -# --- Instalacja / aktualizacja --- +# --- 8. Instalacja / aktualizacja --- log "Pobieranie archiwum z: $APP_URL" mkdir -p "$APP_DIR" TMPZIP=$(mktemp /tmp/karl-app-XXXXXX.zip) @@ -92,20 +92,10 @@ rm -f "$TMPZIP" chown -R karl:karl "$APP_DIR" log "Aplikacja rozpakowana do $APP_DIR" -# --- 7. Sprawdzenie / instalacja uv --- -if command -v uv &>/dev/null; then - log "uv już zainstalowane." -else - log "Instalacja uv..." - pip3 install uv -fi +# --- 9. Instalacja uv i synchronizacja zależności (uv sync) --- +sudo -u karl bash -c "python3 -m venv venv && source venv/bin/activate && pip install uv && uv sync" -# --- 8. Synchronizacja zależności (uv sync) --- -log "Uruchamianie 'uv sync' w katalogu aplikacji..." -cd "$APP_DIR" -sudo -u karl uv sync - -# --- 9. Kopiowanie pliku usługi systemd --- +# --- 10. Kopiowanie pliku usługi systemd --- SERVICE_SRC="$(dirname "$(realpath "$0")")/karl.service" SERVICE_DST="/etc/systemd/system/karl.service" @@ -117,7 +107,7 @@ log "Kopiowanie $SERVICE_SRC -> $SERVICE_DST" cp "$SERVICE_SRC" "$SERVICE_DST" chmod 644 "$SERVICE_DST" -# --- 10. Przeładowanie systemctl i uruchomienie usługi --- +# --- 11. Przeładowanie systemctl i uruchomienie usługi --- log "Przeładowanie systemd i włączanie usługi karl..." systemctl daemon-reload systemctl enable karl.service From 0579527e10339cbf3d264096f63270ded32ec056 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 23:12:40 +0200 Subject: [PATCH 6/9] fix: service file location update --- systemd/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/install.sh b/systemd/install.sh index 0bf7837..c3786ef 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -96,7 +96,7 @@ log "Aplikacja rozpakowana do $APP_DIR" sudo -u karl bash -c "python3 -m venv venv && source venv/bin/activate && pip install uv && uv sync" # --- 10. Kopiowanie pliku usługi systemd --- -SERVICE_SRC="$(dirname "$(realpath "$0")")/karl.service" +SERVICE_SRC="$APP_DIR/systemd/karl.service" SERVICE_DST="/etc/systemd/system/karl.service" if [[ ! -f "$SERVICE_SRC" ]]; then From 97c53a48fb2bb1fd66d337a7cfd1419bbd928a5d Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 23:38:25 +0200 Subject: [PATCH 7/9] fix: uv --- systemd/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/install.sh b/systemd/install.sh index c3786ef..0d14ac5 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -93,7 +93,7 @@ chown -R karl:karl "$APP_DIR" log "Aplikacja rozpakowana do $APP_DIR" # --- 9. Instalacja uv i synchronizacja zależności (uv sync) --- -sudo -u karl bash -c "python3 -m venv venv && source venv/bin/activate && pip install uv && uv sync" +sudo -u karl bash -c "cd /home/karl/app && python3 -m venv .venv && source .venv/bin/activate && pip install uv && uv sync" # --- 10. Kopiowanie pliku usługi systemd --- SERVICE_SRC="$APP_DIR/systemd/karl.service" From 0b3fcdbbc21aaa0c2b59ec5f56c41c8fd4186532 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 8 Apr 2026 23:58:27 +0200 Subject: [PATCH 8/9] fix: install git --- systemd/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/install.sh b/systemd/install.sh index 0d14ac5..6b710b1 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -33,7 +33,7 @@ $PKG_UPDATE # --- 2. Instalacja python3, curl, unzip --- log "Instalacja python3, curl, unzip..." -$PKG_INSTALL python3 python3-pip curl unzip +$PKG_INSTALL python3 python3-pip curl unzip, git $PKG_INSTALL python3-venv || true # --- 3. Instalacja Dockera (jeśli nie zainstalowany) --- From f5bedb97f06edbdfa53ff7f2a925b9d9dd44d952 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Tue, 14 Apr 2026 01:02:04 +0200 Subject: [PATCH 9/9] fix: directory changed --- systemd/install.sh | 7 +++++-- systemd/karl.service | 8 +++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/systemd/install.sh b/systemd/install.sh index 6b710b1..07563b3 100644 --- a/systemd/install.sh +++ b/systemd/install.sh @@ -60,7 +60,10 @@ usermod -aG docker karl # --- 6. Pobranie i rozpakowanie archiwum --- APP_URL="${1:?Podaj URL archiwum ZIP jako pierwszy argument}" -APP_DIR="/home/karl/app" +APP_DIR="/opt/karl" + +mkdir -p "$APP_DIR" +chown karl: "$APP_DIR" # --- 7. Backup i czyszczenie istniejącego katalogu --- if [[ -d "$APP_DIR" ]] && [[ -n "$(ls -A "$APP_DIR" 2>/dev/null)" ]]; then @@ -93,7 +96,7 @@ chown -R karl:karl "$APP_DIR" log "Aplikacja rozpakowana do $APP_DIR" # --- 9. Instalacja uv i synchronizacja zależności (uv sync) --- -sudo -u karl bash -c "cd /home/karl/app && python3 -m venv .venv && source .venv/bin/activate && pip install uv && uv sync" +sudo -u karl bash -c "cd /opt/karl/app && python3 -m venv .venv && source .venv/bin/activate && pip install uv && uv sync" # --- 10. Kopiowanie pliku usługi systemd --- SERVICE_SRC="$APP_DIR/systemd/karl.service" diff --git a/systemd/karl.service b/systemd/karl.service index 9767b65..69f09cc 100644 --- a/systemd/karl.service +++ b/systemd/karl.service @@ -7,10 +7,8 @@ StartLimitIntervalSec=60 [Service] User=karl -WorkingDirectory=/home/karl/app -ExecStart=/home/karl/app/venv/bin/gunicorn \ - -c /home/karl/app/gunicorn.conf.py \ - app.main:app +WorkingDirectory=/opt/karl +ExecStart=/opt/karl/.venv/bin/python3 /opt/karl/src/karl/__init__.py # Graceful reload (sends SIGHUP) ExecReload=/bin/kill -s HUP $MAINPID @@ -23,7 +21,7 @@ RestartSec=5s NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict -ReadWritePaths=/var/log/karl /tmp +ReadWritePaths=/var/log/karl /opt/karl/repository # Logging StandardOutput=journal