Prologen: "Det ska väl bara ta 10 minuter?"
Spoiler alert: Det tog inte 10 minuter.
Jag hade en blogg. Jag ville flytta den till en ny adress för att göra plats åt ett nytt projekt. Klassisk "rockad", tänkte jag. Ladda upp filerna, ändra några sökvägar, klart på en kvart var min förhoppning även om jag vet att saker sällan är så enkla. Mitt problem är att trots att jag för noggranna noteringar om hur jag gör, lösenord och annat så är det alltid något steg som jag missar. Att det inte är samma sak att flytta en Wordpress-blogg (där en faktiskt bara byter sökvägar vid flytt) att flytta en Python-app förstod jag men det blev krångligare än jag räknat med. En viktig anledning är att jag passade på att uppdatera den gamla appen lite och då råkade det följa med lite skräp.
Men efter timmar av felsökning, mystiska felmeddelanden och några "Jaha, SÅ funkar det alltså!"-ögonblick, står nu två sidor där de stod från början och en ny är tillagd men viktigast av allt: jag är lite bättre på att sätta upp Python-appar på shared hosting.
Hjälpreda och lite rörig i huvudet (förutom jag): Claude Sonnet 4.5
Kapitel 1: Pytonkaoset - "Var finns Python 3.13?"
Här är det bra att ha lite koll själv. Även om du talar om vilken prod-miljö du använder så är det inte säkert att AI hittar dokumentationen eller helt enkelt förutsätter att det är samma sökvägar som i dev-miljön. Det visar sig att shared hosting har ofta Python installerat på många ställen.
Lärdomen: "Python 3.13" innebär inte alltid /usr/bin/python3.13. Ofta ligger den i en alternativ katalog. Ta hjälp av dokumentationen.
Kapitel 2: Virtualenv-mysteriet - "Vems venv ska jag använda?"
Nästa utmaning: Ska jag skapa en egen virtuell miljö (venv/) eller använda den som hosten skapar?
Jag gjorde envisa försök att ställa om frågan när AI ville skapa en egen venv men den envisades med att skapa en egen. Om du använder shared hosting så kan det vara så att när du skapar appen så skapas nödvändiga filer och Python-paketen installeras om din requirements.txt är uppdaterad och korrekt.
Lärdomen: Försök inte vara smartare än hosten men var smartare an din AI-agent! De flesta moderna shared hosting-lösningar har automatisering som fungerar - använd den!
Kapitel 3: WSGI-fiaskot - "Den försöker ladda sig själv!"
Den auto-genererade WSGI-filen såg ut så här:
wsgi = load_source('wsgi', 'passenger_wsgi.py')
application = wsgi.application
Även här är det viktigt att ha kontroll över AI-agenten och ifrågasätta vad den gör. Innan jag ville flytta min app var startup-filen main.py och appens entry-point var app. AI envisades med att det skulle finnas en WSGI-fil för att starta appen. Det som ställde till det var troligen också att hosten skapade en WSGI-fil (med kodsnutten ovan) som den inte verkar använda.
Lärdomen: HAMMRAR IN!!! Lita på det du kan och det du vet, AI kan inte allt och ibland är den ganska dum. Moderna hosting-leverantörer har ofta förenklat sina system - använd det! De vill inte drabbas av intrång och krascher och gör vad de kan för att förhindra det.
Kapitel 4: Miljövariabler-epifanin - "Var ÄR de?"
Jag hade lagt in alla mina databas-uppgifter, API-nycklar och hemliga koder i kontrollpanelens miljövariabler. Snyggt och säkert, tänkte jag. Ingen känslig .env-fil på servern som kan läcka.
Sedan ville AI testa via SSH:
flask db upgrade RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' must be set.
Jag: "MEN JAG HAR JU SATT DEN! Den finns i kontrollpanelen!"
SSH och AI: "Jag vet ingenting om det. ¯_(ツ)_/¯"
Två mot en är lite jobbigt. Det tog ett tag för AI att förstå problemet och för mig att förstå vad det var jag försökte göra. Så kom jag på att det är inte en ny databas jag ska sätta upp, databasen finns redan, jag har inte gjort några ändringar i den och det finns innehåll så jag provade helt enkelt att öppna sidan och vips så fungerade den! Alla sidor öppnades som de skulle och jag kunde logga in.
Lärdomen: Kontrollpanelens miljövariabler är BARA tillgängliga när webbservern startar appen. I SSH-sessionen finns de inte. En kan exportera dem tillfälligt för testning:
export DATABASE_URL="mysql+pymysql://user:pass@localhost/db"
Men det är inte nödvändigt för när en väl besöker sidan i webbläsaren - då syns direkt det om det fungerar och vad som inte gör det. Vid det här laget var mina felloggar dessutom igång och det gick att börja felsöka fortsatta problem genom dem.
Miljövariabler från kontrollpanelen är för produktionsmiljön (webben), inte för SSH-testning. Skippa SSH-testerna och testa direkt i webbläsaren istället!
Kapitel 5: Emoji-katastrofen - "💾 kan inte sparas!"
Allt fungerade. Sidan laddades. Databasen pratade med Flask. Jag försökte skapa ett inlägg och...
UnicodeEncodeError: 'ascii' codec can't encode character '\U0001f4be'
Jag: "Inte nu igen!"
Det visade sig vara en emoji. En liten diskett-emoji (💾) som hade lagts in i en print()-sats som AI lagt in för att göra loggarna roligare:
print("💾 Spara innehåll")
Servern använder ASCII-encoding för stdout. Emojis är UTF-8. Går inte ihop. Kraschar.
Lösningen: Ta bort alla emojis från print()-satser.
print("Spara innehåll") # Tråkigt men funkar
Lärdomen: AI-agenter, oavsett vilken vi pratar om, gillar emojis för de har lärt sig att det är kul och lättar upp texten. I server-logs är de fienden. Se till att rensa bort allt sådant till prod, i dev kan det bli överväldigande att rensa bort vid varje ändring för rensar jag så kommer det snart nya. Jag fick göra ganska många rensningar innan alla sidor fungerade felfritt igen.
Epilogen: "Nu funkar allt!"
Efter denna odyssé har jag:
- ✅ Fungerande Python-appar på rätt platser
- ✅ Bättre kunskap om hur jag sätter upp en app på min webbhost
- ✅ Nya och förhoppningvis mer detaljerade checklistor för framtida deployment
När det krånglar
Kolla alltid error log först:
De flesta hosting-leverantörer har error logs i en katalog som ~/logs/error_log eller liknande. De flesta problem har tydliga felmeddelanden där. Läs dem noga. Googla dem. Lita på felmeddelandena.
De vanligaste felen:
- "Can't find module" → requirements.txt saknar paketet
- "Can't connect to MySQL" → Databas-uppgifter är fel i miljövariabler
- "SQLALCHEMY_DATABASE_URI must be set" → Miljövariabler inte konfigurerade
- "Maximum recursion depth" → WSGI-filen försöker ladda sig själv
Slutord
Nu vet jag bättre hur systemet fungerar. Förhoppningsvis går det fortare och lättare nästa gång även om jag tvivlar lite men för varje försök kommer jag några steg längre i min checklista innan jag fastnar :-)
PS: Jag lät Claude sammanfatta vår chatt för ett blogginlägg. Jag har skrivit om det mesta och rensat en massa blaj men låtit emojisarna vara kvar som illustration :-)