To finałowy, dziesiąty odcinek kursu ASP.NET Core. Masz aplikację MVC z kontrolerami, widokami, Entity Frameworkiem i migracjami. Na localhost:5001 wszystko śmiga. Tylko co z tego, skoro nikt poza tobą tego nie zobaczy? Pora wypchnąć kod w świat. Pokażę ci trzy drogi, a potem przeprowadzę przez jedną z nich od A do Z — razem z bazą, FTP-em i migracjami na produkcji.
Trzy drogi na produkcję
Zanim włączysz Visual Studio i klikniesz "Publish", ustal gdzie właściwie chcesz wylądować. W świecie .NET masz trzy sensowne warianty.
Azure. Jeśli jesteś studentem i masz darmowe kredyty, albo pracujesz w firmie, która już siedzi w ekosystemie Microsoftu, to pierwszy wybór. Deployment z Visual Studio jest bajeczny — klik-klik i aplikacja jedzie. Wada? Kosztuje swoje. Przy małych, osobistych projektach rachunek za App Service + SQL Database potrafi zirytować. Do nauki idealne, na dłuższą metę do pet-projectów ja omijam.
VPS + reverse proxy. Kupujesz wirtualną maszynę (DigitalOcean, Hetzner, Contabo — co wolisz), instalujesz .NET runtime, uruchamiasz aplikację lokalnie na localhost:5000. I tu jest haczyk: .NET sam z siebie nie wystawi ci tego na świat. Potrzebujesz warstwy przed Kestrelem — Nginx albo Apache — który zrobi reverse proxy z portu 80/443 na twoje 5000. To rozwiązanie produkcyjne, używane w poważnych setupach. Plus pełna kontrola, minus konfiguracja, certyfikaty, systemd unity, logi.
Kestrel to świetny serwer aplikacyjny, ale nie jest frontem dla świata. Zawsze stawiaj przed nim Nginx, Apache albo IIS — na produkcji bezpieczeństwo i TLS to nie opcja.
Shared hosting. Trzeci wariant, ten który pokażę w praktyce. Polski hosting z obsługą .NET (używam webio, serio polecam) kosztuje okolice 60 zł rocznie. Dostajesz kilka baz MSSQL, kilka domen, certyfikaty Let's Encrypt w jednym kliknięciu i support, który odpisuje po polsku. Dla małej aplikacji pokazowej albo portfolio to optymalny stosunek ceny do spokoju.
Publish z Visual Studio
Zostawmy teorię. Otwierasz projekt, klikasz prawym na projekcie webowym i wybierasz Publish. Wizard zapyta cię o target. Masz tam Azure, Docker, IIS, Folder, FTP i kilka innych. Ja wybieram Folder — generuje mi paczkę na dysku, którą potem sam wrzucam gdzie chcę.
W ustawieniach profilu warto zerknąć na:
- Configuration:
Release(nie Debug — produkcja ma być szczupła i zoptymalizowana). - Target Framework: to samo, co w projekcie, np.
net6.0. - Deployment Mode:
Framework-Dependent(mniejsza paczka, wymaga .NET na serwerze) alboSelf-Contained(wszystko w środku, większy rozmiar, ale uruchomisz to wszędzie). - Target Runtime: jeśli wiesz, że hosting to Windows —
win-x64. Linux VPS —linux-x64.
Z linii komend to samo załatwisz w sekundę:
dotnet publish -c Release -o ./publish
Albo z samodzielnym runtime, jeśli nie chcesz polegać na tym, co siedzi na serwerze:
dotnet publish -c Release --self-contained -r win-x64 -o ./publish
Po publish w folderze bin/Release/net6.0/publish lądują wszystkie pliki, które faktycznie lecą na serwer — DLL-ki, wwwroot, appsettings.json, web.config. To jest twoja paczka deploymentowa.
Self-contained vs framework-dependent
Jedna decyzja, którą warto podjąć świadomie. Framework-dependent zakłada, że serwer ma zainstalowany ASP.NET Core Runtime w odpowiedniej wersji. Paczka waży kilka MB, szybko się wgrywa, aktualizujesz runtime osobno. Minus: jeśli hosting nie ma twojej wersji albo jest opóźniony w łataniu, masz problem.
Self-contained pakuje runtime razem z aplikacją. Paczka puchnie do kilkudziesięciu MB, ale jest niezależna — uruchomisz ją na czystej maszynie bez niczego doinstalowanego. Na VPS-ach, gdzie sam zarządzasz środowiskiem, to często wygodniejsze. Na shared hostingu raczej framework-dependent — hostingodawca dba o runtime.
FTP na shared hosting
Skoro lecimy przez shared, scenariusz jest taki:
- Logujesz się do panelu hostingu i zakładasz witrynę (albo używasz tej darmowej testowej końcówki, którą dostajesz w pakiecie).
- Tworzysz bazę MSSQL — najpierw użytkownika z hasłem, potem samą bazę i przypinasz do niej tego użytkownika.
- Instalujesz darmowy certyfikat SSL (Let's Encrypt, jeden klik w panelu).
- Zakładasz konto FTP — login, hasło, folder docelowy (zwykle
wwwalbo podobnie). - Odpalasz klienta FTP (ja używam FileZilla), łączysz się, czyścisz folder
wwwz plików placeholderowych hostingu. - Wrzucasz zawartość swojego folderu
publishdowww.
I to praktycznie tyle po stronie plików. IIS na hostingu przeczyta web.config z twojej paczki, znajdzie w nim aspNetCore handler, odpali proces dotneta i będzie robił reverse proxy. web.config w paczce publish jest generowany automatycznie — nie musisz go pisać ręcznie, chyba że chcesz coś customizować (zmienne środowiskowe, reguły URL Rewrite, limity).
Jeśli po wrzuceniu plików dostajesz HTTP 500.30 albo białą stronę — 90% przypadków to nieprawidłowa wersja .NET runtime na serwerze, zły
web.configalbo brakująca zmienna środowiskowa. Zajrzyj dostdoutlogów IIS, są gadatliwe.
Przed wrzucaniem plików warto zatrzymać witrynę w panelu (stop site) — unikniesz błędów typu "plik zablokowany, bo proces go trzyma". Po wgraniu — start site i ładujesz domenę w przeglądarce.
Migracje na produkcyjną bazę
To jest punkt, który wielu początkujących podpala. Lokalnie masz bazę z aktualnym schematem — bo codziennie ją migrujesz. Na serwerze masz pustą bazę, świeżo utworzoną. Jeśli wrzucisz aplikację i ją odpalisz, Entity Framework zapyta o tabele, których jeszcze nie ma, i dostaniesz exception.
Rozwiązań jest kilka, ale najprostsze dla początków: uruchom migracje z lokalnej maszyny, wskazując connection string produkcyjny. Otwierasz appsettings.json lokalnie (albo lepiej — tymczasowy plik poza repo), wklejasz connection stringa z panelu hostingu:
Server=mssql.webio.pl;Database=test_baza;User Id=jozef;Password=TwojeHaslo;TrustServerCertificate=True
Odpalasz Package Manager Console:
Update-Database
Migracje lecą nie na lokalną bazę, tylko na produkcyjną. Po chwili masz tam wszystkie tabelki. Wracasz connection string do lokalnego i idziesz dalej.
W dojrzalszym setupie dorzucasz do startu aplikacji context.Database.Migrate() albo używasz dedykowanego narzędzia (DbUp, Flyway), które odpala migracje przy starcie kontenera/procesu. Ale na etapie kursu ręczny Update-Database wystarczy aż nadto.
Connection string w production
Tu uwaga absolutnie kluczowa.
Production connection string NIGDY nie trafia do
appsettings.jsoncommitowanego w repo. Nigdy. Ani jako plaintext, ani "tymczasowo", ani "bo to tylko GitHub prywatny".
Masz kilka sensownych miejsc na produkcyjny connection string:
appsettings.Production.json— osobny plik, którego nie commitujesz (dodaj do.gitignore), wgrywasz bezpośrednio przez FTP na serwer obok pozostałych plików aplikacji.- Zmienne środowiskowe — na IIS ustawiasz przez
web.config(sekcjaenvironmentVariables) albo przez panel hostingu. Klucz typuConnectionStrings__DefaultConnection.NET Core rozpoznaje automatycznie. - Azure Key Vault / AWS Secrets Manager / User Secrets (ten ostatni tylko na developmencie).
W .NET Core appsettings.{Environment}.json nadpisuje appsettings.json automatycznie, kiedy zmienna ASPNETCORE_ENVIRONMENT=Production jest ustawiona. IIS domyślnie ustawia Production, więc to zadziała out of the box.
Co dalej?
To był ostatni odcinek kursu ASP.NET Core — Podstawy. Masz aplikację od pustego projektu, przez kontrolery, widoki, bazę i migracje, aż po publikację na serwerze produkcyjnym. Jeśli chcesz zobaczyć końcowy stan kodu, zajrzyj na GitHuba z lekcją 10.
Co dalej można zrobić z tym kursowym projektem?
- CI/CD przez GitHub Actions — żeby publish + upload na FTP leciał automatycznie po każdym pushu do main.
- Dockerize i deploy na VPS — obraz Dockera, Nginx reverse proxy, Let's Encrypt, systemd. Realistyczny produkcyjny setup.
- Azure deployment — z App Service i Azure SQL, żeby zobaczyć jak to wygląda po stronie chmury enterprise.
- Autentykacja przez Identity i role — żeby ta aplikacja miała sensowny login, panel admina i zabezpieczenia kontrolerów.
- API zamiast MVC — refactoring tego samego domain modelu do WebAPI + osobny frontend (np. Blazor, React).
Daj znać w komentarzu, który kierunek cię kręci — może zrobimy z tego kolejną serię. Dzięki za przejście przez cały kurs i do usłyszenia w następnej.



