Dealownia
Menu
sprawdzono – jak sprawdzamy oferty

Serwer pod agenta AI 24/7: czy wystarczy VPS za 20 zł

Agent AI na API modeli w chmurze działający całą dobę: co zużywa RAM i CPU, systemd albo Docker, klucz API poza zmiennymi, limity wydatków u dostawcy i logi.

AI i modelezaktualizowano 10 min czytania

Redakcja serwisu Dealownia · ceny i fakty ze stron dostawców, jak sprawdzamy

Zwykle tak. Agent, który myśli modelem w chmurze (przez API Anthropic, OpenAI albo innego dostawcy), sam niczego nie liczy – wysyła zapytanie, czeka na odpowiedź, wykonuje narzędzie i znowu czeka. Przez większość czasu proces stoi na połączeniu sieciowym, a procesor jest bezczynny. Najmniejszy VPS z 1–2 GB RAM uciągnie takiego agenta, dopóki agent nie zacznie uruchamiać ciężkich narzędzi: przeglądarki, kompilacji, lokalnych modeli. Rachunek za API zależy od liczby tokenów, a nie od serwera, i przy agencie pracującym bez nadzoru łatwo przerasta cenę VPS-a. Dlatego ważniejsze od rozmiaru planu są limity wydatków i to, żeby klucz API nie wyciekł.

Ten poradnik nie dotyczy lokalnego modelu – jeśli model ma działać na serwerze, wymagania są zupełnie inne i opisuje je poradnik Ollama na VPS bez GPU.

Co naprawdę zużywa zasoby

Element RAM CPU Uwagi
Sam proces agenta (Python, Node.js) z SDK dostawcy mało prawie zero w czasie czekania na API rośnie z historią rozmów trzymaną w pamięci
Przeglądarka bez okna (Playwright, Chromium) dużo, każda karta osobno skoki przy renderowaniu stron dokumentacja Playwrighta ostrzega, że Chromium w Dockerze bez --ipc=host może zabraknąć pamięci i się wysypać
Wykonywanie kodu, testów, kompilacji zależy od projektu wysokie w trakcie najczęstszy powód, dla którego mały VPS przestaje wystarczać
Lokalna baza wektorowa, embeddingi liczone na serwerze średnio do dużo wysokie przy indeksowaniu embeddingi z API przenoszą ten koszt do dostawcy
Kilka zadań naraz mnoży wszystko powyżej ogranicz współbieżność w kodzie
Logi – – zajmują dysk, jeśli nie ustawisz limitów (krok 5)

Zamiast zgadywać, zmierz: uruchom agenta na najmniejszym planie i zostaw na dobę typowej pracy. systemctl status agent pokazuje zużycie pamięci usługi (w nowszych wersjach systemd także szczytowe), systemd-cgtop – wszystkie usługi naraz, docker stats – kontenery. Jeśli szczyt zbliża się do RAM-u serwera, weź większy plan albo przenieś ciężkie narzędzie gdzie indziej. Tanie plany porównasz w rankingu VPS do 20 zł i w pełnym rankingu VPS.

Krok 1. Osobny użytkownik i katalog

Zakładamy serwer przygotowany jak w poradniku od zera. Agent, który potrafi wykonywać polecenia albo pisać pliki, nie powinien działać jako Twój użytkownik z sudo – błąd w promptach albo złośliwa treść, którą agent przeczyta na stronie, może skłonić go do zrobienia czegoś, czego nie chciałeś. Ogranicz szkody z góry:

bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin agent
sudo mkdir -p /opt/apps/agent
sudo chown deploy:deploy /opt/apps/agent
cd /opt/apps/agent
git clone https://github.com/twoje-konto/twoj-agent.git app
python3 -m venv .venv
.venv/bin/pip install -r app/requirements.txt

Kod należy do deploy, a agent go tylko czyta. Dane, które agent zapisuje, trafią do osobnego katalogu stanu (krok 3).

Krok 2. Klucz API w pliku poświadczeń, nie w zmiennej środowiskowej

Najczęstsza praktyka to klucz w pliku .env wczytywanym jako zmienne środowiskowe. Dokumentacja systemd ocenia to wprost: zmienne środowiskowe nie nadają się do przekazywania sekretów – te ustawione w jednostce są widoczne dla nieuprzywilejowanych klientów przez D-Bus, a wszystkie dziedziczą procesy potomne. Dla agenta, który uruchamia podprocesy (narzędzia, skrypty, przeglądarkę), to szczególnie ważne: każdy z nich dostaje klucz w swoim środowisku, a stamtąd łatwo trafia do logów, zrzutów błędów czy wyniku polecenia env. Zalecany mechanizm to poświadczenia systemd (LoadCredential=).

Uczciwie trzeba dodać, czego poświadczenia nie załatwiają: plik z kluczem jest czytelny dla użytkownika usługi, więc podproces działający jako agent może go odczytać, jeśli celowo go poszuka. Poświadczenia usuwają klucz ze środowiska i z przypadkowych wycieków, ale nie izolują go od kodu uruchamianego przez agenta. Przed tym chronią dopiero osobny klucz z niskim limitem wydatków (krok 4) albo uruchamianie narzędzi w osobnym kontenerze lub jako inny użytkownik.

Plik z kluczem, czytelny tylko dla roota (wpisz klucz w edytorze, nie przez echo, żeby nie został w historii powłoki):

bash
sudo mkdir -p /etc/agent
sudo install -m 600 -o root -g root /dev/null /etc/agent/api_key
sudo nano /etc/agent/api_key

W kodzie agent czyta klucz z katalogu, który systemd przekazuje w zmiennej $CREDENTIALS_DIRECTORY, i podaje go klientowi SDK jawnie. Przykład dla SDK Anthropic w Pythonie (domyślnie czyta ono klucz ze zmiennej ANTHROPIC_API_KEY, ale przyjmuje go też w parametrze api_key):

python
import os
from pathlib import Path
import anthropic

key = (Path(os.environ["CREDENTIALS_DIRECTORY"]) / "api_key").read_text().strip()
client = anthropic.Anthropic(api_key=key)

SDK innych dostawców mają analogiczny parametr klucza w konstruktorze klienta.

Dwie zasady niezależne od sposobu przechowywania:

  • Osobny klucz dla serwera. Nie używaj tego samego klucza co na laptopie. Po wycieku unieważniasz jeden klucz i nic innego nie przestaje działać.
  • Osobna przestrzeń rozliczeń. U Anthropic załóż dla agenta osobny workspace w Claude Console i wygeneruj w nim klucz – limity wydatków ustawisz wtedy tylko dla niego (krok 4). U OpenAI to samo robi osobny projekt.

Krok 3. Usługa systemd

Plik /etc/systemd/system/agent.service:

ini
[Unit]
Description=Agent AI
After=network-online.target
Wants=network-online.target

[Service]
User=agent
Group=agent
WorkingDirectory=/opt/apps/agent/app
ExecStart=/opt/apps/agent/.venv/bin/python main.py
LoadCredential=api_key:/etc/agent/api_key
StateDirectory=agent
Restart=on-failure
RestartSec=30

# ograniczenia zasobów
MemoryHigh=700M
MemoryMax=800M
CPUQuota=80%

# ograniczenia uprawnień
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now agent
systemctl status agent
journalctl -u agent -f

Co tu jest ważne:

  • LoadCredential=api_key:/etc/agent/api_key – systemd sam odczytuje plik roota i udostępnia go procesom usługi w katalogu z $CREDENTIALS_DIRECTORY (według dokumentacji dostęp ma tylko użytkownik usługi i root). Użytkownik agent nie musi mieć dostępu do /etc/agent.
  • StateDirectory=agent – katalog /var/lib/agent z prawem zapisu dla agenta. Przy ProtectSystem=strict cały system plików jest tylko do odczytu, więc to jedyne miejsce, w którym agent może pisać (pliki robocze, baza SQLite, pamięć rozmów).
  • MemoryMax= i MemoryHigh= – według dokumentacji MemoryMax to twardy limit (po jego przekroczeniu zadziała OOM killer wewnątrz usługi, a nie na całym serwerze), a MemoryHigh to próg, powyżej którego procesy są mocno spowalniane. Wartości dobierz do planu – na serwerze z 1 GB RAM przykładowe zostawiają trochę pamięci dla systemu i SSH.
  • CPUQuota=80% – najwyżej 80% jednego rdzenia. Pętla, która się zawiesi na liczeniu, nie zablokuje serwera.
  • Restart=on-failure z RestartSec=30 – agent wstaje po awarii, ale nie częściej niż co 30 sekund. Celowo nie always: agent, który poprawnie kończy pracę, nie powinien startować w pętli.
  • Gdy agent ma uruchamiać przeglądarkę albo kompilować kod, sprawdź, czy narzędzia działają przy ProtectSystem=strict – mogą potrzebować dodatkowych katalogów zapisu (ReadWritePaths=).

Prostszą wersję tej samej usługi – z tokenem w pliku .env wczytywanym przez EnvironmentFile= – opisuje poradnik o bocie Discorda 24/7. Dla bota, który nie uruchamia cudzych poleceń ani podprocesów, to rozsądny kompromis; dla agenta, który je uruchamia, lepsze są poświadczenia.

Wariant w Dockerze

Jeśli agent działa w kontenerze, odpowiednikiem poświadczeń są sekrety Compose. Dokumentacja Dockera zwraca uwagę na ten sam problem co systemd: zmienne środowiskowe widzą wszystkie procesy w kontenerze i łatwo trafiają do logów. Plik /opt/apps/agent/compose.yaml:

yaml
services:
  agent:
    build: ./app
    restart: unless-stopped
    init: true
    mem_limit: 800m
    cpus: 0.8
    secrets:
      - api_key
    volumes:
      - agent-data:/data
    logging:
      driver: local

secrets:
  api_key:
    file: /etc/agent/api_key

volumes:
  agent-data:

Compose montuje plik w kontenerze jako /run/secrets/api_key – kod czyta go z tej ścieżki zamiast z $CREDENTIALS_DIRECTORY. Bez Docker Swarm sekret z pliku jest zwykłym zamontowanym plikiem z uprawnieniami takimi jak na hoście: jeśli proces w kontenerze nie działa jako root (a nie powinien), plik root:root z prawami 600 będzie dla niego nieczytelny. Nadaj plikowi na hoście właściciela o tym samym UID, którego używa kontener, i zostaw prawa 600. init: true zapobiega procesom zombie, co dokumentacja Playwrighta zaleca przy przeglądarce w kontenerze. Jeśli agent używa Chromium, dodaj też ipc: host. Kontener nie ma sekcji ports, bo agent łączy się tylko na zewnątrz.

Krok 4. Limity wydatków u dostawcy API

To najważniejszy krok. Agent działający całą dobę bez nadzoru może wpaść w pętlę: błąd narzędzia, ponowienie, ta sama odpowiedź, kolejne ponowienie – i każde z tych wywołań kosztuje. Limit na serwerze tego nie zatrzyma, limit u dostawcy tak.

Anthropic (Claude API). Według dokumentacji (stan na 12.11.2026):

  • każdy poziom konta ma miesięczny limit wydatków organizacji (np. 500 USD na poziomie Start); własny, niższy limit ustawisz w Claude Console w Settings → Spend limits;
  • można ustawić osobne limity wydatków i zapytań dla workspace’ów – z wyjątkiem domyślnego workspace’u, dlatego agent powinien mieć własny;
  • po osiągnięciu limitu ustawionego przez Ciebie API zwraca błąd 400 invalid_request_error z komunikatem o osiągniętym limicie, a po osiągnięciu limitu poziomu – 429 bez nagłówka retry-after. W tym drugim przypadku ponawianie, także automatyczne ponowienia SDK, nie pomoże aż do początku kolejnego miesiąca (00:00 UTC pierwszego dnia) albo przejścia na wyższy poziom; w treści błędu jest kod enforced_spend_limit_reached.

OpenAI. Według dokumentacji limity ustawisz dla organizacji i dla projektu. Alerty wysyłają tylko powiadomienie i nie zatrzymują ruchu. Zatrzymuje go dopiero twardy limit (opcja „Enforce a hard limit”) – wtedy zapytania dostają błąd 429 z kodem project_spend_limit_exceeded albo organization_spend_limit_exceeded. Dokumentacja zastrzega, że egzekwowanie nie jest natychmiastowe, więc wydatki mogą nieznacznie przekroczyć limit.

W kodzie agenta dołóż własne bezpieczniki, bo limit miesięczny chroni przed katastrofą, a nie przed zmarnowaniem połowy budżetu w jedną noc:

  • maksymalna liczba kroków (wywołań modelu) na jedno zadanie;
  • max_tokens w każdym zapytaniu;
  • przy błędzie limitu wydatków – zatrzymanie pracy i powiadomienie zamiast ponawiania;
  • licznik tokenów z pola usage odpowiedzi, zapisywany w logu przy każdym zadaniu.

Ile kosztują tokeny i jak policzyć miesięczny rachunek w złotych, pokazuje poradnik ile kosztuje API modeli AI. Jeśli agent ma służyć tylko Tobie do pracy na co dzień, porównaj też abonamenty w rankingu asystentów AI.

Krok 5. Logi, które nie zapchają dysku

Agent loguje dużo: zapytania, odpowiedzi, wyniki narzędzi. Na małym dysku to realny problem.

  • journald. Według dokumentacji domyślny limit to 10% systemu plików (najwyżej 4 GB), a czasowe usuwanie wpisów jest domyślnie wyłączone. Na VPS-ie z 20 GB dysku ustaw limit jawnie w pliku /etc/systemd/journald.conf.d/60-limit.conf:
ini
[Journal]
SystemMaxUse=500M
MaxRetentionSec=1month

Katalog nadpisań zwykle trzeba najpierw utworzyć: sudo mkdir -p /etc/systemd/journald.conf.d. Potem:

bash
sudo systemctl restart systemd-journald
journalctl --disk-usage
  • Docker. Domyślny sterownik json-file według dokumentacji nie rotuje logów. Sterownik local (jak w przykładzie wyżej) rotuje je domyślnie.
  • Czego nie logować. Klucza API (także w komunikatach wyjątków z konfiguracją klienta), pełnych treści dokumentów z danymi osobowymi i haseł, które agent mógł zobaczyć w trakcie pracy. Loguj identyfikator zadania, liczbę kroków, zużyte tokeny i błędy.

Krok 6. Sygnał, że agent żyje

Proces może działać, a agent i tak stać – np. zawieszony na zapytaniu bez limitu czasu albo zatrzymany przez limit wydatków. Niech agent po każdym udanym cyklu wywołuje adres monitora typu push w Uptime Kuma. Gdy wywołania ustaną, dostaniesz alert. Pozostałe zabezpieczenia serwera – SSH tylko z kluczem, zapora, aktualizacje – są w liście kontrolnej.

Najczęstsze problemy

  • KeyError: 'CREDENTIALS_DIRECTORY'. Proces uruchomiony poza systemd (np. ręcznie do testu) albo brak LoadCredential= w usłudze. Testuj przez sudo systemctl restart agent i journalctl -u agent -n 50, a nie uruchamiając skrypt ręcznie.
  • Usługa ginie, a w journalctl jest „oom-kill”. Przekroczony MemoryMax. Sprawdź, co rośnie – historia rozmowy w pamięci, przeglądarka, równoległe zadania – i albo to ogranicz, albo podnieś limit i plan.
  • Agent stoi, błędy 429. Rozróżnij limit zapytań (jest nagłówek retry-after, wystarczy poczekać) od limitu wydatków (u Anthropic bez retry-after, u OpenAI kod …_spend_limit_exceeded). Na limit wydatków nie pomaga ponawianie.
  • Narzędzie nie może zapisać pliku: „Read-only file system”. Skutek ProtectSystem=strict. Zapisuj w /var/lib/agent albo dodaj katalog w ReadWritePaths=.
  • Rachunek za API większy niż zakładany. Przejrzyj w logach liczbę kroków na zadanie. Najczęściej winna jest pętla ponowień albo zbyt długa historia wysyłana przy każdym kroku.

Źródła

Sprawdzone 12.11.2026:

Powiązane poradniki