Dodano: 04.05.2026 / Zaktualizowano: 09.05.2026
Instalacja i konfiguracja strony internetowej na VPS z Ubuntu 24.04 LTS
Bazy danychKonfiguracjaLinux | UnixPrywatneWWW
Spis treści
Stanowisko komputerowe z dwoma monitorami – strona IT i panel analityczny

Minąło trochę ponad 1,5 roku od ostatniej rekonfiguracji mojej strony internetowej i dodatkowych systemów na zmienionych parametrach VPS z Ubuntu 22.04 LTS. W między czasie zmienił się właściciel serwisu hosting.linux.pl i z tą zmianą przyszły dość spore podwyżki cen (w moim przypadku cena VPS`a wzrosła o 400%). Hosting poczty i domen na najbliższy rok zostanie tam gdzie był (w tym przypadku cena również wzrosła), natomiast w przypadku VPS postanowiłem przejść do innego usługodawcy i po długim badaniu rynku wybrałem webh.pl.

Podobnie jak poprzednio, najnowsza wersja v3 VPS jest bazą dla moich kilku systemów:

  1. grzegorzwita.it - strona główna,
  2. zycie.grzegorzwita.it - prywatny notatnik oparty o system BookStack,
  3. wiki.grzegorzwita.it - prywatny zbiór porad dla ekosystemu Microsoftu oparty o system Wiki.js,
  4. system.grzegorzwita.it - mały CRM oparty o system Invoice Ninja,
  5. grzegorzwita.it\statystyka - alias do pliku statystyk Apache`a oparty o system GoAccess.
  6. grzegorzwita.it\phpmyadmin oraz FTP - usługi dla agencji, która ogarnia mi stronę internetową.

Sama migracja z Ubuntu 22.04 LTS do 24.04 LTS przeszła bez większych problemów.

Parametry VPS i testy wydajności

Parametry sprzętowe w "v3" poraz kolejny uległy polepszeniu we wszystkich trzech aspektach czyli CPU, RAM i SSD. Obecne parametry to:

  • 2 rdzenie
  • 8 GB RAM
  • 100 GB NVMe SSD

Wynik testy dd (maksymalna przepustowość sekwencyjna dysku oraz wydajność przy małych blokach (IOPS) + wpływ generowania losowych danych):

dd if=/dev/zero of=test bs=1M count=1024 conv=fdatasync oflag=direct
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.680626 s, 1.6 GB/s
dd if=/dev/urandom of=test bs=4k count=250000 conv=fdatasync oflag=direct
250000+0 records in
250000+0 records out
1024000000 bytes (1.0 GB, 977 MiB) copied, 28.6482 s, 35.7 MB/s

Wynik testu YABS`a:

# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #
#              Yet-Another-Bench-Script              #
#                     v2026-04-29                    #
# https://github.com/masonr/yet-another-bench-script #
# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #

Mon May  4 07:25:25 PM CEST 2026

Basic System Information:
---------------------------------
Uptime     : 2 days, 10 hours, 0 minutes
Processor  : AMD EPYC-Milan Processor
CPU cores  : 2 @ 3193.998 MHz
AES-NI     : ✔ Enabled
VM-x/AMD-V : ❌ Disabled
RAM        : 7.8 GiB
Swap       : 512.0 MiB
Disk       : 98.3 GiB
Distro     : Ubuntu 24.04.4 LTS
Kernel     : 6.8.0-111-generic
VM Type    : KVM
IPv4/IPv6  : ✔ Online / ❌ Offline

IPv4 Network Information:
---------------------------------
ISP        : IWACOM Sp. z o.o
ASN        : AS41508 ZINET.NET.PL Sp. z.o.o.
Host       : ULTIMAHOST.PL SZELIGA sp. j
Location   : Lodz, Łódź Voivodeship (10)
Country    : Poland

fio Disk Speed Tests (Mixed R/W 50/50) (Partition /dev/vda2):
---------------------------------
Block Size | 4k            (IOPS) | 64k           (IOPS)
  ------   | ---            ----  | ----           ----
Read       | 126.62 MB/s  (31.6k) | 575.26 MB/s   (8.9k)
Write      | 126.95 MB/s  (31.7k) | 578.28 MB/s   (9.0k)
Total      | 253.58 MB/s  (63.3k) | 1.15 GB/s    (18.0k)
           |                      |
Block Size | 512k          (IOPS) | 1m            (IOPS)
  ------   | ---            ----  | ----           ----
Read       | 1.33 GB/s     (2.6k) | 477.54 MB/s    (466)
Write      | 1.41 GB/s     (2.7k) | 509.34 MB/s    (497)
Total      | 2.75 GB/s     (5.3k) | 986.89 MB/s    (963)

iperf3 Network Speed Tests (IPv4):
---------------------------------
Provider        | Location (Link)           | Send Speed      | Recv Speed      | Ping
-----           | -----                     | ----            | ----            | ----
Clouvider       | London, UK (10G)          | 192 Mbits/sec   | busy            | 32.9 ms
Eranium         | Amsterdam, NL (100G)      | 193 Mbits/sec   | 170 Mbits/sec   | 23.2 ms
Uztelecom       | Tashkent, UZ (10G)        | 183 Mbits/sec   | 116 Mbits/sec   | 121 ms
Leaseweb        | Singapore, SG (10G)       | 164 Mbits/sec   | 111 Mbits/sec   | 253 ms
Clouvider       | Los Angeles, CA, US (10G) | 176 Mbits/sec   | busy            | 151 ms
Leaseweb        | NYC, NY, US (10G)         | 184 Mbits/sec   | 139 Mbits/sec   | 101 ms
Edgoo           | Sao Paulo, BR (1G)        | 171 Mbits/sec   | 55.4 Mbits/sec  | 196 ms

Geekbench 6 Benchmark Test:
---------------------------------
Test            | Value
                |
Single Core     | 1299
Multi Core      | 2321
Full Test       | https://browser.geekbench.com/v6/cpu/17874778

YABS completed in 12 min 49 sec

Wynik testu PHP Benchmark Script:

-------------------------------------------------------
|       PHP BENCHMARK SCRIPT v.2.0 by @SergiX44       |
-------------------------------------------------------
PHP............................................. 8.3.30
Platform......................................... Linux
Arch............................................ x86_64
Server................................. u259085.webh.me
Max memory usage.................................... -1
OPCache status................................ disabled
OPCache JIT....................... disabled/unavailable
PCRE JIT....................................... enabled
XDebug extension.............................. disabled
Difficulty multiplier............................... 1x
Started at..................... 04/05/2026 17:41:55.814
-------------------------------------------------------
math.......................................... 0.2162 s
loops......................................... 0.1251 s
ifelse........................................ 0.2508 s
switch........................................ 0.1697 s
string........................................ 0.3898 s
array......................................... 0.5664 s
regex......................................... 0.3284 s
is_{type}..................................... 0.3084 s
hash.......................................... 0.1430 s
json.......................................... 0.2369 s
-------------------------------------------------------
Total time.................................... 2.7346 s
Peak memory usage................................ 2 MiB

Wynik testu cyberfolks-dbSpeedTest (z moją małą poprawką):

+---------------------------------------------------------------+----------+--------+
| Typ operacji                                                  | Szybkość | Ocena  |
+---------------------------------------------------------------+----------+--------+
| Wstawianie rekordów                                           |   291/s  | Dobra  |
|   Przykład: dodanie nowego artykułu, złożenie zamówienia,     |          |        |
|   rejestracja nowego klienta                                  |          |        |
+---------------------------------------------------------------+----------+--------+
| Wyszukiwanie rekordu po kluczu głównym                        |  2424/s  | Dobra  |
|   Przykład: znalezienie produktu po ID w e-commerce,          |          |        |
|   artykułu po ID w CMS                                        |          |        |
+---------------------------------------------------------------+----------+--------+
| Wyszukiwanie nieindeksowanych rekordów całkowitych (dokładne) |   545/s  | Dobra  |
|   Przykład: znalezienie wszystkich koszul w rozmiarze 40      |          |        |
+---------------------------------------------------------------+----------+--------+
| Wyszukiwanie tekstowe (ciąg 3 znaków w rekordach)             |    91/s  | Dobra  |
|   Przykład: znalezienie klientów, których imię zawiera 'art'  |          |        |
+---------------------------------------------------------------+----------+--------+
| Wyszukiwanie nieindeksowanych rekordów tekstowych (dokładne)  |   454/s  | Dobra  |
|   Przykład: wyszukiwanie wszystkich przedmiotów               |          |        |
|   w danym kolorze                                             |          |        |
+---------------------------------------------------------------+----------+--------+
| Selekcja rekordów z warunkiem: wartość_pola * 1.23 > losowa   |   235/s  | Słaba  |
|   Przykład: wyszukiwanie produktów większych niż...           |          |        |
+---------------------------------------------------------------+----------+--------+
| Aktualizacja rekordów                                         |    91/s  | Dobra  |
|   Przykład: zmiana koloru wszystkich przedmiotów z zielonego  |          |        |
|   na niebieski                                                |          |        |
+---------------------------------------------------------------+----------+--------+

Wstępne przygotowanie systemu

Loguję się na VPS na konto root za pomocą wygenerowanego hasła, które od razu zmieniam na swoje własne. Następnie tworzę konto dla siebie i dodaję je do grupy sudo.

adduser nazwa_użytkownika
usermod -aG sudo nazwa_użytkownika

Przelogowuję się na swoje konto, a następnie:

  • aktualizuję system i pakiety do najnowszych dostępnych wersji,
    sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get dist-upgrade -y && sudo apt-get autoremove -y
  • ustawiam odpowiednią strefę czasową,
    sudo dpkg-reconfigure tzdata
  • instaluję dodatkowe oprogramowanie.
    sudo apt-get install apt-transport-https unzip -y
    sudo wget https://7-zip.org/a/7z2501-linux-x64.tar.xz && sudo tar -xf 7z2501-linux-x64.tar.xz -C /usr/bin/ 7zz && sudo rm 7z2501-linux-x64.tar.xz

SSH

  1. Przygotowuję klucz z hasłem.
    ssh-keygen -t ed25519 -o -a 100
  2. Przygotowuję katalog dla klucza.
    touch .ssh/authorized_keys
  3. Kopiuję klucz publiczny do authorized_keys, a następnie pobieram klucz prywatny na swój komputer np. za pomocą WinSCP.
    cat .ssh/id_ed25519.pub >> .ssh/authorized_keys
  4. Usuwam wygenerowane pliki klucza SSH.
    rm .ssh/id_ed25519*
  5. Ustawiam odpowiednie uprawnienia dla pliku authorized_keys.
    chmod 600 .ssh/authorized_keys
  6. Aktualizuję konfigurację SSH i modyfikuję poniższe pozycje.
    sudo nano /etc/ssh/sshd_config
    [...]
    #Include /etc/ssh/sshd_config.d/*.conf
    [...]
    Port numer_portu
    [...]
    PermitRootLogin no
    [...]
    PubkeyAuthentication yes
    [...]
    AuthorizedKeysFile .ssh/authorized_keys
    [...]
    PasswordAuthentication no
    PermitEmptyPasswords no
    [...]
    ClientAliveInterval 300
    ClientAliveCountMax 3
    [...]
    
  7. Zmieniam konfigurację ListenStream dla SSH.
    sudo nano /etc/systemd/system/sockets.target.wants/ssh.socket
    [...]
    [Socket]
    ListenStream=0.0.0.0:22122
    [...]
  8. Włączam zaporę sieciową w systemie.
    sudo ufw enable
  9. Udostępniam port SSH na zaporze sieciowej.
    sudo ufw allow from any to any port numer_portu proto tcp
  10. Restartuję demona Systemd.
    sudo systemctl daemon-reload
  11. Restartuję usługę SSH.
    sudo systemctl restart ssh

Apache

  1. Dodaję repozytorium PPA dla PHP.
    LC_ALL=C.UTF-8 sudo add-apt-repository ppa:ondrej/php
  2. Instaluję PHP 8.3.
    sudo apt-get install php8.3 -y
  3. Instaluję dodatkowe rozszerzenia dla PHP 8.3.
    sudo apt-get install php8.3-{mysql,fpm,curl,mbstring,ldap,xml,zip,gd,gmp,tidy,imagick,bcmath,bz2,intl,soap,xmlrpc} -y
  4. Tworzę katalog dla plików strony internetowej.
    sudo mkdir /var/www/grzegorzwitait
  5. Rozpakowuję archiwum kopii zapasowej strony, a następnie przenoszę pliki do utworzonego folderu.
    sudo tar -xvf grzegorzwitait_2025-10-07.tar
    sudo mv -f /home/nazwa_użytkownika/PLIKI/var/www/grzegorzwitait/{.,}* /var/www/grzegorzwitait/
  6. Ustawiam rekurencyjnie uprawnienia dla katalogów i plików dla użytkownika i grupy www-data.
    sudo chown -R www-data:www-data /var/www/grzegorzwitait/
  7. Ustawiam uprawnienia 755 dla wszystkich katalogów oraz 644 dla wszystkich plików.
    sudo find /var/www/grzegorzwitait/ -type d -exec chmod 755 {} \;
    sudo find /var/www/grzegorzwitait/ -type f -exec chmod 644 {} \;
  8. Ustawiam uprawnienia 775 dla wszystkich katalogów oraz 664 dla wszystkich plików w lokalizacjach do których dodawane są zdjęcia i pliki.
    sudo find /var/www/grzegorzwitait/files/page_files/* -type d -exec chmod 775 {} \;
    sudo find /var/www/grzegorzwitait/files/page_files/* -type f -exec chmod 664 {} \;
    sudo find /var/www/grzegorzwitait/images/page_images/foto_* -type d -exec chmod 775 {} \;
    sudo find /var/www/grzegorzwitait/images/page_images/foto_* -type f -exec chmod 664 {} \;
  9. Aktualizuję dostępy do bazy danych w pliku konfiguracyjnym oraz ustawiam uprawnienie 740 dla tego pliku.
    sudo nano /var/www/grzegorzwitait/database.php
    sudo chmod 740 /var/www/grzegorzwitait/database.php
  10. Zmieniam właściciela na root oraz grupę na www-data dla katalogu nadrzędnego.
    sudo chown root:www-data /var/www/grzegorzwitait/
  11. Ustawiam konfigurację strony. Jeśli miałbym kopię zapasową konfiguracji to przekopiowałbym ją do folderu Apache i ustawił odpowiednie uprawnienia. Niestety tego nie zrobiłem się musiałem przygotować nowy plik.
    sudo mv -f /home/$USER/PLIKI/grzegorzwitait.conf /etc/apache2/sites-available/ && sudo chown root:root /etc/apache2/sites-available/grzegorzwitait.conf
    sudo nano /etc/apache2/sites-available/grzegorzwitait.conf
    <VirtualHost *:80>
        ServerName grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
        
        RewriteEngine On
        RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,QSA,R=permanent]
    </VirtualHost>
    
    <IfModule mod_ssl.c>
    SSLStaplingCache "shmcb:/var/log/ssl_stapling(150000)"
    <VirtualHost *:443>
        Protocols h2 http/1.1
        ServerName grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
    
        DocumentRoot /var/www/grzegorzwitait/
        DirectoryIndex index.php
    
        Redirect permanent /statystyka https://grzegorzwita.it/statystyka.html
    
        <Directory /var/www/grzegorzwitait/>
            Options -Indexes -Includes +FollowSymLinks +MultiViews
    	    AllowOverride all
    	    Require all granted
        </Directory>
        
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
        Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        Header always set X-Frame-Options "SAMEORIGIN"
        Header always set X-XSS-Protection "1; mode=block"
        Header always set X-Content-Type-Options "nosniff"
        Header always set Referrer-Policy "strict-origin-when-cross-origin"
        Header always set Content-Security-Policy "form-action 'self'; base-uri 'self'; frame-ancestors 'self'; object-src 'none'; worker-src 'self'; child-src 'self'; frame-src 'self'; upgrade-insecure-requests"
    
        SSLEngine on
        SSLProtocol -all +TLSv1.2 +TLSv1.3
        SSLCipherSuite EECDH+AESGCM:EDH+AESGCM
        SSLOpenSSLConfCmd Curves X25519:secp521r1:secp384r1:prime256v1
        SSLOpenSSLConfCmd Options -SessionTicket,ServerPreference    
        SSLHonorCipherOrder off
        SSLSessionTickets off
        SSLCompression off
        SSLUseStapling on
        SSLCertificateFile /etc/letsencrypt/live/grzegorzwita.it/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/grzegorzwita.it/privkey.pem
    </VirtualHost>
    </IfModule>
  12. Ustawiam uprawnienia root:root dla pliku konfiguracyjnego strony.
    sudo chown root:root /etc/apache2/sites-available/grzegorzwitait.conf
  13. Włączam konfigurację strony.
    sudo a2ensite grzegorzwitait.conf
  14. Wyłączam konfigurację domyślnej strony Apache`a.
    sudo a2dissite 000-default.conf
  15. Wyłączam niepotrzebne moduły PHP.
    sudo a2dismod php8.3 mpm_prefork
  16. Włączam potrzebne moduły PHP.
    sudo a2enmod rewrite headers http2 mpm_event proxy_fcgi proxy_http ssl
  17. Włączam konfigurację PHP-FPM.
    sudo a2enconf php8.3-fpm
  18. Zmieniam ustawienia PHP-FPM.
    sudo sed -i "s/;date.timezone =/date.timezone = Europe\/Warsaw/" /etc/php/8.3/fpm/php.ini
    sudo sed -i "s/memory_limit = 128M/memory_limit = 1024M/" /etc/php/8.3/fpm/php.ini
    sudo sed -i "s/upload_max_filesize = 2M/upload_max_filesize = 6M/" /etc/php/8.3/fpm/php.ini
    sudo sed -i "s/max_execution_time = 30/max_execution_time = 60/" /etc/php/8.3/fpm/php.ini
    sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/" /etc/php/8.3/fpm/php.ini
    sudo sed -i "s/max_file_uploads = 20/max_file_uploads = 40/" /etc/php/8.3/fpm/php.ini
  19. Restartuję usługę PHP-FPM.
    sudo systemctl restart php8.3-fpm.service
  20. Udostępniam porty Apache`a w zaporze sieciowej.
    sudo ufw allow from any to any port 80,443 proto tcp

MySQL

  1. Instaluję serwer MySQL.
    sudo apt-get install mysql-server -y
  2. Uruchamiam konfigurator serwera MySQL.
    sudo mysql_secure_installation
  3. Loguję się do serwera MySQL.
    sudo mysql -u root -p
  4. Tworzę bazę danych dla strony internetowej.
    CREATE DATABASE grzegorzwitait_dba;
  5. Tworzę użytkownika do bazy danych dla strony internetowej.
    CREATE USER 'grzegorzwitait_dba'@'localhost' IDENTIFIED WITH caching_sha2_password BY '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
  6. Nadaję odpowiednie uprawnienia utworzonemu użytkownikowi.
    GRANT ALL ON grzegorzwitait_dba.* TO 'grzegorzwitait_dba'@'localhost';
  7. Wprowadzam zmiany w życie.
    FLUSH PRIVILEGES;
  8. Wychodzę z serwera bazy danych.
    EXIT
  9. Importuję bazę danych z kopii zapasowej.
    sudo mysql -u root -p grzegorzwitait_dba < /home/nazwa_użytkownika/PLIKI/grzegorzwitait_*.sql

SSL

  1. Instaluję odpowiednie pakiety.
    sudo apt-get install certbot python3-certbot-apache -y
  2. Generuję certyfikat Wildcard i postępuję zgodnie z informacjami wyświetlanymi na ekranie.
    sudo certbot certonly --manual --preferred-challenges=dns --email admin@grzegorzwita.it --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d "*.grzegorzwita.it, grzegorzwita.it"
  3. Sprawdzam certyfikaty.
    sudo certbot certificates
  4. Przygotowuję skrypt, który będę wykorzystywał przy odświeżaniu certyfikatu oraz ustawiam uprawnienie 740, abym mógł go uruchamiać.
    sudo nano /root/letsencrypt_renew.sh
    #!/bin/bash
    sudo certbot certonly --manual --preferred-challenges=dns --email admin@grzegorzwita.it --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d "*.grzegorzwita.it, grzegorzwita.it"
    sudo systemctl reload apache2
    sudo chmod 740 /root/letsencrypt_renew.sh
  5. Sprawdzam poprawność konfiguracji Apache`a.
    sudo apache2ctl configtest
  6. Restartuję usługę Apache`a.
    sudo systemctl restart apache2
  7. Weryfikuję zabezpieczenia strony na https://www.ssllabs.com/ssltest/.

phpmyadmin

  1. Loguję się do serwera MySQL.
    sudo mysql -u root -p
  2. Tworzę bazę danych dla phpmyadmin.
    CREATE DATABASE grzegorzwitait_pma;
  3. Tworzę użytkownika do bazy danych dla phpmyadmin.
    CREATE USER 'grzegorzwitait_pma'@'localhost' IDENTIFIED WITH caching_sha2_password BY '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
  4. Nadaję odpowiednie uprawnienia utworzonemu użytkownikowi dla bazy danych phpmyadmin oraz strony internetowej.
    GRANT ALL ON grzegorzwitait_pma.* TO 'grzegorzwitait_pma'@'localhost';
    GRANT ALL ON grzegorzwitait_dba.* TO 'grzegorzwitait_pma'@'localhost';
  5. Wprowadzam zmiany w życie.
    FLUSH PRIVILEGES;
  6. Tymczasowo usuwam komponent bazy danych.
    UNINSTALL COMPONENT "file://component_validate_password";
  7. Wychodzę z serwera bazy danych.
    EXIT
  8. Instaluję pakiet phpmyadmin ale nie tworzę bazy danych za pomocą narzędzia dbconfig-common.
    sudo apt-get install phpmyadmin -y
  9. Ponownie loguję się do serwera bazy danych MySQL.
    sudo mysql -u root -p
  10. Przywracam wcześniej usunięty komponent bazy danych.
    INSTALL COMPONENT "file://component_validate_password";
  11. Wychodzę z serwera bazy danych.
    EXIT
  12. Aktualizuję konfigurację phpmyadmin.
    sudo nano /etc/phpmyadmin/config.inc.php
    [...]
    /* User for advanced features */
    $cfg['Servers'][$i]['controluser'] = 'grzegorzwitait_pma';
    $cfg['Servers'][$i]['controlpass'] = '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
    [...]
  13. Aktualizuję konfigurację Apache`a.
    sudo nano /etc/apache2/conf-available/phpmyadmin.conf
    <Directory /usr/share/phpmyadmin>
        [. . .]
        AllowOverride All
    
        [. . .]
    </Directory>
  14. Zmieniam wersję PHP z 8.4 na 8.3, a następnie włączam moduł mbstring.
    sudo update-alternatives --config php
    sudo phpenmod mbstring
  15. Restartuję usługę Apache`a.
    sudo systemctl restart apache2
  16. W przeglądarce internetowej wchodzę na adres grzegorzwita.it/phpmyadmin i loguję się danymi wprowadzonymi podczas konfiguracji. Po poprawnym zalogowaniu się zaznaczam odpowiednią bazę danych, a następnie w menu u samej góry klikam w Operacje / odnośnik tutaj / odnośnik Create  i dosłownie po niespełna sekundzie baza danych zostanie przygotowana.

FTP

  1. Instaluję pakiet vsftpd.
    sudo apt-get install vsftpd -y
  2. Udostępniam na zaporze sieciowej porty 20, 21 oraz zakres portów od 40 000 do 50 000.
    sudo ufw delete allow from any to any port 20:21,40000:50000 proto tcp comment 'FTP'
  3. Aktualizuję konfigurację FTP.
    sudo nano /etc/vsftpd.conf
    [...]
    listen=YES
    [...]
    listen_ipv6=NO
    [...]
    write_enable=YES
    [...]
    local_umask=022
    [...]
    chroot_local_user=YES
    [...]
    
    allow_writeable_chroot=YES
    force_dot_files=YES
    pasv_min_port=40000
    pasv_max_port=50000
  4. Restartuję usługę FTP.
    sudo systemctl restart vsftpd.service
  5. Sprawdzam status działania usługi FTP.
    sudo systemctl status vsftpd.service
  6. Tworzę dedykowane konto FTP wraz z lokalizacją, która będzie dla niego widoczna.
    sudo adduser --home /var/www/grzegorzwitait/ --ingroup www-data nazwa_uzytkownika
  7. Uprawnienia na potrzeby pracy na FTP.
    sudo find /var/www/grzegorzwitait/ -type d -exec chmod 775 {} \;
    sudo find /var/www/grzegorzwitait/ -type f -exec chmod 664 {} \;

BookStack

  1. Instaluję pakiet composer.
    sudo apt-get install composer -y
  2. Tworzę katalog na potrzeby Bookstack.
    sudo mkdir /var/www/BookStack
  3. Rozpakowuję archiwum kopii zapasowej i przenoszę pliki do katalogu docelowego.
    sudo tar -xvf BookStack_2025-10-07.tar
    sudo mv -f /home/$USER/PLIKI/BookStack/{.,}* /var/www/BookStack/
  4. Ustawiam rekurencyjnie uprawnienia plików i katalogów na użytkownika i grupę www-data.
    sudo chown -R www-data:www-data /var/www/BookStack/
  5. Ustawiam uprawnienia katalogów na 755, a plików na 644.
    sudo find /var/www/BookStack/ -type d -exec chmod 755 {} \;
    sudo find /var/www/BookStack/ -type f -exec chmod 644 {} \;
  6. Aktualizuję plik konfiguracyjny.
    sudo nano /var/www/BookStack/.env
  7. Ustawiam uprawnienia pliku konfiguracyjnego na 740.
    sudo chmod 740 /var/www/BookStack/.env
  8. Ustawiam uprawnienia plików na 775 w trzech lokalizacjach.
    sudo chmod 775 /var/www/BookStack/bootstrap/cache/ /var/www/BookStack/public/uploads/ /var/www/BookStack/storage/
  9. Zmieniam właściciela i grupę na root dla katalogu nadrzędnego.
    sudo chown root:root /var/www/BookStack/
  10. Ustawiam rekurencyjnie właściciela i grupę na root dla katalogów i plików w katalogach repozytorium.
    sudo chown -R root:root /var/www/BookStack/.git
    sudo chown -R root:root /var/www/BookStack/.github
  11. Loguję się do serwera MySQL.
    sudo mysql -u root -p
  12. Tworzę bazę danych dla Bookstack.
    CREATE DATABASE bookstack;
  13. Tworzę użytkownika do bazy danych dla Bookstack.
    CREATE USER 'bookstack_dba'@'localhost' IDENTIFIED WITH caching_sha2_password BY '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
  14. Nadaję odpowiednie uprawnienia utworzonemu użytkownikowi.
    GRANT ALL ON bookstack.* TO 'bookstack_dba'@'localhost';
  15. Wprowadzam zmiany w życie.
    FLUSH PRIVILEGES;
  16. Wychodzę z serwera bazy danych.
    EXIT
  17. Importuję bazę danych z kopii zapasowej.
    sudo mysql -u root -p bookstack < /home/$USER/PLIKI/grzegorzwitait_*.sql
  18. Przechodzę do katalogu Bookstack`a.
    cd /var/www/BookStack/
  19. Resetuję git`a.
    sudo git reset --hard HEAD
  20. Pobieram aktualną wersję programu z repozytorium Github`a.
    sudo git pull origin release
  21. Instaluję zależności composer`a.
    sudo composer install --no-dev --no-plugins
  22. Aktualizuję bazę danych oraz wykonuję czyszczenie.
    sudo php artisan migrate && sudo php artisan cache:clear && sudo php artisan config:clear && sudo php artisan view:clear
  23. Tworzę konfigurację strony dla Apache`a.
    sudo mv -f /home/$USER/PLIKI/bookstack.conf /etc/apache2/sites-available/ && sudo chown root:root /etc/apache2/sites-available/bookstack.conf
    sudo nano /etc/apache2/sites-available/bookstack.conf
    <VirtualHost *:80>
        ServerName zycie.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
        Redirect / https://zycie.grzegorzwita.it
    </VirtualHost>
    
    <VirtualHost *:443>
        ServerName zycie.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
    
        DocumentRoot /var/www/BookStack/public/
    
        <Directory /var/www/BookStack/public/>
            Options Indexes FollowSymLinks
            AllowOverride None
            Require all granted
            <IfModule mod_rewrite.c>
                <IfModule mod_negotiation.c>
                    Options -MultiViews -Indexes
                </IfModule>
                RewriteEngine On
                # Handle Authorization Header
                RewriteCond %{HTTP:Authorization} .
                RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
                # Redirect Trailing Slashes If Not A Folder...
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteCond %{REQUEST_URI} (.+)/$
                RewriteRule ^ %1 [L,R=301]
                # Handle Front Controller...
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteCond %{REQUEST_FILENAME} !-f
                RewriteRule ^ index.php [L]
            </IfModule>
        </Directory>
    
        ErrorLog ${APACHE_LOG_DIR}/error_bookstack.log
        CustomLog ${APACHE_LOG_DIR}/access_bookstack.log combined
    
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/grzegorzwita.it/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/grzegorzwita.it/privkey.pem
    </VirtualHost>
  24. Włączam konfigurację strony i restartuję usługę Apache`a.
    sudo a2ensite bookstack.conf
  25. BookStack`a w przyszłości aktualizuję w poniższy sposób.
    cd /var/www/BookStack/ && sudo git pull origin release && sudo composer install --no-dev && sudo php artisan migrate && sudo php artisan cache:clear && sudo php artisan config:clear && sudo php artisan view:clear

Wiki.js

  1. Dodaję repozytorium dla node.js w wersji 22.
    sudo curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
  2. Instaluję pakiet nodejs.
    sudo apt-get install nodejs -y
  3. Tworzę katalog dla Wiki.js.
    sudo mkdir /var/www/Wikijs
  4. Rozpakowuję archiwum kopii zapasowej strony, a następnie przenoszę pliki do utworzonego folderu.
    sudo tar -xvf Wikijs_2025-10-07
    sudo mv -f /home/nazwa_użytkownika/PLIKI/var/www/Wikijs/{.,}* /var/www/Wikijs/
  5. Loguję się do serwera MySQL.
    sudo mysql -u root -p
  6. Tworzę bazę danych dla Wiki.js.
    CREATE DATABASE wikijs;
  7. Tworzę użytkownika do bazy danych dla Wiki.js.
    CREATE USER 'wikijs_dba'@'localhost' IDENTIFIED WITH caching_sha2_password BY '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
  8. Nadaję odpowiednie uprawnienia utworzonemu użytkownikowi.
    GRANT ALL ON wikijs.* TO 'wikijs_dba'@'localhost';
  9. Wprowadzam zmiany w życie.
    FLUSH PRIVILEGES;
  10. Wychodzę z serwera bazy danych.
    EXIT
  11. Aktualizuję plik konfiguracyjny.
    sudo nano config.yml
    [...]
    db:
      type: mysql
    
      #PostreSQL / MySQL / MariaDB / MS SQL Server only:
      host: localhost
      port: 3306
      user: wikijs_dba
      pass: 8arDzo_Sk0mp1ikow4N3_Ha$LO!
      db: wikijs
      ssl: false
    
    [...]
  12. Ustawiam rekurencyjnie właściciela i grupę dla katalogów i plików www-data.
    sudo chown -R www-data:www-data /var/www/Wikijs/
  13. Ustawiam uprawnienia dla katalogów na 755, a plików na 644.
    sudo find /var/www/Wikijs/ -type d -exec chmod 755 {} \;
    sudo find /var/www/Wikijs/ -type f -exec chmod 644 {} \;
  14. Ustawiam uprawnienia na 740 dla pliku konfiguracyjnego.
    sudo chmod 740 /var/www/Wikijs/config.yml
  15. Ustawiam właściciela i grupę na root dla katalogu nadrzędnego.
    sudo chown root:root /var/www/Wikijs/
  16. Przygotowuję plik serwisu dla Wiki.js.
    sudo nano /etc/systemd/system/wikijs.service
    [Unit]
    Description=Wiki.js
    After=network.target
    
    [Service]
    Type=simple
    ExecStart=/usr/bin/node server
    Restart=always
    
    User=root
    Environment=NODE_ENV=production
    WorkingDirectory=/var/www/Wikijs
    
    [Install]
    WantedBy=multi-user.target
  17. Przelogowuję demona systemctl.
    sudo systemctl daemon-reload
  18. Włączam usługę Wiki.js.
    sudo systemctl enable wikijs
  19. Uruchamiam usługę Wiki.js.
    sudo systemctl start wikijs
  20. Sprawdzam status usługi Wiki.js.
    sudo systemctl status wikijs
  21. Tworzę konfigurację strony dla Apache`a.
    sudo nano /etc/apache2/sites-available/wikijs.conf
    <VirtualHost *:80>
        ServerName wiki.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
        Redirect / https://wiki.grzegorzwita.it
    </VirtualHost>
    
    <VirtualHost *:443>
        ServerName wiki.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
    
        DocumentRoot /var/www/Wikijs/
        ProxyPass / http://127.0.0.1:3000/
        ProxyPassReverse / http://127.0.0.1:3000/
    
        ErrorLog ${APACHE_LOG_DIR}/error_wikijs.log
        CustomLog ${APACHE_LOG_DIR}/access_wikijs.log combined
    
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/grzegorzwita.it/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/grzegorzwita.it/privkey.pem
    </VirtualHost>
  22. Włączam konfigurację strony.
    sudo a2ensite wikijs.conf
  23. Restartuję usługę Apache`a.
    sudo systemctl restart apache2
  24. Wiki.js w przyszłości aktualizuję w poniższy sposób.
    cd /var/www/Wikijs/ && sudo systemctl stop wikijs && sudo mv config.yml ~/config.yml && sudo rm -rf * && sudo wget https://github.com/Requarks/wiki/releases/latest/download/wiki-js.tar.gz && sudo tar xzf wiki-js.tar.gz && sudo rm wiki-js.tar.gz && sudo mv ~/config.yml ./config.yml && sudo chown -R www-data:www-data /var/www/Wikijs/ && sudo find /var/www/Wikijs/ -type d -exec chmod 755 {} \; && sudo find /var/www/Wikijs/ -type f -exec chmod 644 {} \; && sudo chown root:root /var/www/Wikijs/ && sudo chmod 740 /var/www/Wikijs/config.yml && sudo systemctl start wikijs

Przechodzę do przeglądarki na skonfigurowany adres i dokańczam konfigurację.

Invoice Ninja

  1. Przygotowuję katalog na potrzeby Invoice Ninja.
    sudo mkdir /var/www/InvoiceNinja
  2. Pobieram archiwum systemu z repozytorium (w moim przypadku była to wersja 5.10.8, w chwili kiedy to czytasz na pewno dostępna jest już nowsza wersja).
    sudo wget https://github.com/invoiceninja/invoiceninja/releases/download/v5.10.8/invoiceninja.tar
  3. Rozpakowuję archiwum programu do katalogu docelowego.
    sudo tar xzf invoiceninja.tar -C /var/www/InvoiceNinja/
  4. Usuwam pobrane archiwum.
    sudo rm invoiceninja.tar
  5. Loguję się do serwera MySQL.
    sudo mysql -u root -p
  6. Tworzę bazę danych dla Invoice Ninja.
    CREATE DATABASE invoiceninja;
  7. Tworzę użytkownika do bazy danych dla Invoice Ninja.
    CREATE USER 'invoiceninja_dba'@'localhost' IDENTIFIED WITH caching_sha2_password BY '8arDzo_Sk0mp1ikow4N3_Ha$LO!';
  8. Nadaję odpowiednie uprawnienia utworzonemu użytkownikowi.
    GRANT ALL ON invoiceninja.* TO 'invoiceninja_dba'@'localhost';
  9. Wprowadzam zmiany w życie.
    FLUSH PRIVILEGES;
  10. Wychodzę z serwera bazy danych.
    EXIT
  11. Przygotowuję plik konfiguracyjny.
    sudo cp /var/www/InvoiceNinja/.env.example /var/www/InvoiceNinja/.env
  12. Ustawiam plik konfiguracyjny.
    sudo nano /var/www/InvoiceNinja/.env
    [...]
    DB_HOST=localhost
    DB_DATABASE=invoiceninja
    DB_USERNAME=invoiceninja_dba
    DB_PASSWORD=8arDzo_Sk0mp1ikow4N3_Ha$LO!
    DB_PORT=3306
    [...]
    PDF_GENERATOR=snappdf
    [...]
    PHANTOMJS_SECRET="8arDzo_Sk0mp1ikow4N3_Ha$LO!"
    
    UPDATE_SECRET="8arDzo_Sk0mp1ikow4N3_Ha$LO!"
    WEBCRON_SECRET="8arDzo_Sk0mp1ikow4N3_Ha$LO!"
    [...]
    EXPANDED_LOGGING=true
  13. Ustawiam rekurencyjnie właściciela i grupę dla wszystkich katalogów i plików na www-data.
    sudo chown -R www-data:www-data /var/www/InvoiceNinja/
  14. Ustawiam uprawnienia dla katalogów na 755, a plików na 644.
    sudo find /var/www/InvoiceNinja/ -type d -exec chmod 755 {} \;
    sudo find /var/www/InvoiceNinja/ -type f -exec chmod 644 {} \;
  15. Ustawiam uprawnienia na 740 dla pliku konfiguracyjnego.
    sudo chmod 740 /var/www/InvoiceNinja/.env
  16. Ustawiam właściciela i grupę na root dla katalogu nadrzędnego.
    sudo chown root:root /var/www/InvoiceNinja/
  17. Tworzę konfigurację strony dla Apache`a.
    sudo nano /etc/apache2/sites-available/invoiceninja.conf
  18. Aktualizuję i ustawiam odpowiednie uprawnienia dla snappdf w celu naprawy generowania podglądu plików PDF w systemie. Do wersji 5.10.13 każda instalacja lub aktualizacja systemu (np. z wersji 5.10.8 do 5.10.10) wymagała ponownego wykonania poniższych poleceń. Na nowszych wersjach (np. aktualizacja z 5.10.10 do 5.10.26) nie trzeba już tego robić.
    sudo chmod 744 /var/www/InvoiceNinja/vendor/beganovich/snappdf/snappdf && sudo -u www-data /var/www/InvoiceNinja/vendor/beganovich/snappdf/snappdf --force download && sudo chmod 644 /var/www/InvoiceNinja/vendor/beganovich/snappdf/snappdf
  19. Instaluję dodatkowe pakiety wymagane przez snappdf.
    sudo apt-get install fonts-liberation libasound2 libatk1.0-0 libatk-bridge2.0-0 libgbm1 libpango1.0-0 libxcomposite1 libxdamage1 libxfixes3 libxkbcommon0 libxrandr2 -y
  20. Aktualizuję plik hosts.
    sudo sed -i "s/127.0.0.1 localhost/127.0.0.1 localhost system.grzegorzwita.it/" /etc/hosts
  21. Dodanę cron`y dla użytkownika www-data.
    sudo -u www-data crontab -e
    [...]
    #InvoiceNinja
    #0 8 * * * php /var/www/InvoiceNinja/artisan ninja:send-recurring >> /dev/null
    #0 8 * * * php /var/www/InvoiceNinja/artisan ninja:send-reminders >> /dev/null
    * * * * * php /var/www/InvoiceNinja/artisan schedule:run >> /dev/null 2>&1
    * * * * * php /var/www/InvoiceNinja/artisan optimize >> /dev/null 2>&1
  22. Przechodzę do katalogu Invoice Ninja.
    cd /var/www/InvoiceNinja/
  23. Generuję klucz.
    sudo -u www-data php artisan key:generate
  24. Uzbrajam bazę danych.
    sudo -u www-data php artisan migrate
  25. Optymalizuję konfigurację.
    sudo -u www-data php artisan optimize
  26. Tworzę konfigurację strony dla Apache`a.
    sudo mv -f /home/$USER/PLIKI/invoiceninja.conf /etc/apache2/sites-available/ && sudo chown root:root /etc/apache2/sites-available/invoiceninja.conf
    sudo nano /etc/apache2/sites-available/invoiceninja.conf
    <VirtualHost *:80>
        ServerName system.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
        Redirect / https://system.grzegorzwita.it
    </VirtualHost>
    
    <VirtualHost *:443>
        ServerName system.grzegorzwita.it
        ServerAdmin admin@grzegorzwita.it
    
        DocumentRoot /var/www/InvoiceNinja/public/
    
        <Directory /var/www/InvoiceNinja/public/>
            DirectoryIndex index.php
            Options +FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>
    
        ErrorLog ${APACHE_LOG_DIR}/error_invoiceninja.log
        CustomLog ${APACHE_LOG_DIR}/access_invoiceninja.log combined
    
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/grzegorzwita.it/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/grzegorzwita.it/privkey.pem
    </VirtualHost>
  27. Włączam konfigurację strony.
    sudo a2ensite invoiceninja.conf
  28. Restartuję usługę Apache`a.
    sudo systemctl restart apache2

GoAccess

  1. Instaluję pakiet.
    sudo apt-get install goaccess -y
  2. Przygotowuję konfigurację.
    sudo nano /root/goaccess.sh
    #!/bin/bash
    goaccess /var/log/apache2/access.log -o /var/www/grzegorzwitait/statystyka.html --log-format=COMBINED --real-time-html --persist --restore --db-path /var/www/grzegorzwitait/tmp/ --real-os --all-static-files --ssl-cert=/etc/letsencrypt/live/grzegorzwita.it/fullchain.pem --ssl-key=/etc/letsencrypt/live/grzegorzwita.it/privkey.pem
  3. Ustawiam uprawnienia pliku na 740, abym mógł go uruchamiać.
    sudo chmod 740 /root/goaccess.sh
  4. Ustawiam cron`a.
    sudo crontab -e
    [...]
    @reboot /root/goaccess.sh >> dev
    
  5. Udostępniam na zaporze sieciowej port 7890.
    sudo ufw allow from any to any port 7890 proto tcp

Kopia zapasowa

Zaktualizowałem swój pierwotny skrypt kopia_zapasowa.sh, aby wykonywał kopię głównej strony internetowej wraz z wszystkimi subdomenami oraz ich bazami danych.

Napiszę tylko, że na obecnym VPS czas wykonania kopii zapasowej ponad 172 000 plików rozproszonych w ponad 19 400 folderów oraz 4 baz danych o łącznej wadze przekraczającej 2,82 GB (spakowany plik *.7z ma ok. 586 MB) trwa 25 minut.

  1. Tworzę katalog do przechowywania plików kopii zapasowej i logów w swoim katalogu domowym.
    mkdir /home/$USER/backup
  2. Przygotowuję skrypt w wybranym katalogu.
    sudo nano /root/kopia_zapasowa.sh
    #!/bin/bash
    DATE=$(date +"%Y-%m-%d")
    
    tar -caf - /var/www/grzegorzwitait -P | 7zz a -si /var/www/grzegorzwitait.tar.7z
    tar -caf - /var/www/BookStack -P | 7zz a -si /var/www/BookStack.tar.7z
    tar -caf - /var/www/Wikijs -P | 7zz a -si /var/www/Wikijs.tar.7z
    tar -caf - /var/www/InvoiceNinja -P | 7zz a -si /var/www/InvoiceNinja.tar.7z
    7zz e /var/www/grzegorzwitait.tar.7z -o/var/www/
    7zz e /var/www/BookStack.tar.7z -o/var/www/
    7zz e /var/www/Wikijs.tar.7z -o/var/www/
    7zz e /var/www/InvoiceNinja.tar.7z -o/var/www/
    rm -f -R /var/www/*.tar.7z
    mv /var/www/grzegorzwitait.tar /var/www/grzegorzwitait_$DATE.tar
    mv /var/www/BookStack.tar /var/www/BookStack_$DATE.tar
    mv /var/www/Wikijs.tar /var/www/Wikijs_$DATE.tar
    mv /var/www/InvoiceNinja.tar /var/www/InvoiceNinja_$DATE.tar
    mysqldump --host=localhost --user=root --password='8arDzo_Sk0mp1ikow4N3_Ha$LO!' grzegorzwitait_dba > /var/www/mysql_grzegorzwitait_$DATE.sql
    mysqldump --host=localhost --user=root --password='8arDzo_Sk0mp1ikow4N3_Ha$LO!' bookstack > /var/www/mysql_bookstack_$DATE.sql
    mysqldump --host=localhost --user=root --password='8arDzo_Sk0mp1ikow4N3_Ha$LO!' wikijs > /var/www/mysql_wikijs_$DATE.sql
    mysqldump --host=localhost --user=root --password='8arDzo_Sk0mp1ikow4N3_Ha$LO!' invoiceninja > /var/www/mysql_invoiceninja_$DATE.sql
    7zz a -t7z -mhe=on -pmN1gdy_nie-zgadni3szhaha -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on /var/www/$USER_$DATE.7z /var/www/*.tar /var/www/*.sql
    mv /var/www/$USER_$DATE.7z /home/$USER/backup/
    find /home/$USER/backup/ -type f -mtime +7 -delete
    find /var/www/ -maxdepth 1 -type f -delete
  3. Ustawiam cron job`a, aby uruchamiał skrypt codziennie o 20:00.
    sudo crontab -e
    0 20 * * * /root/kopia_zapasowa.sh >> /home/$USER/backup/kopia_zapasowa_`date +\%Y-\%m-\%d`.log 2>&1
  4. Ustawiam uprawnienia pliku na 740, aby plik był wykonywalny.
    sudo chmod 740 /root/kopia_zapasowa.sh
0 komentarzy

Szybki kontakt

Masz pytania? Napisz