Publikacja ASP.NET Core na hostingu — od publish do live

Publikacja ASP.NET Core w trzech wariantach — Azure, VPS z reverse proxy i tani shared hosting z FTP. Connection string, migracje i web.config bez zgadywania.

JAJarosław Armatys 8 min czytania
Wersja wideo tego wpisu — obejrzyj na YouTube

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) albo Self-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:

  1. Logujesz się do panelu hostingu i zakładasz witrynę (albo używasz tej darmowej testowej końcówki, którą dostajesz w pakiecie).
  2. Tworzysz bazę MSSQL — najpierw użytkownika z hasłem, potem samą bazę i przypinasz do niej tego użytkownika.
  3. Instalujesz darmowy certyfikat SSL (Let's Encrypt, jeden klik w panelu).
  4. Zakładasz konto FTP — login, hasło, folder docelowy (zwykle www albo podobnie).
  5. Odpalasz klienta FTP (ja używam FileZilla), łączysz się, czyścisz folder www z plików placeholderowych hostingu.
  6. Wrzucasz zawartość swojego folderu publish do www.

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.config albo brakująca zmienna środowiskowa. Zajrzyj do stdout logó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.json commitowanego 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 (sekcja environmentVariables) albo przez panel hostingu. Klucz typu ConnectionStrings__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.

Tagi:#course-aspnet#dotnet#hosting#deployment

Masz pytanie do tego wpisu?

Napisz do mnie — chętnie porozmawiam o szczegółach technicznych, albo o tym jak zastosować to u Ciebie w projekcie.