Plik .env przechowuje zmienne środowiskowe w prostym formacie klucz-wartość i oddziela konfigurację od kodu źródłowego aplikacji. Według The Twelve-Factor App (2011), konfiguracja powinna być ściśle oddzielona od kodu, a pliki .env stały się standardem tego podejścia. .env File pozwala podstawiać różne wartości kluczy API, URL serwera i flag kompilacji bez ponownej kompilacji projektu.
Najważniejsze
.env File to plik konfiguracyjny, w którym przechowywane są zmienne środowiskowe w prostym formacie tekstowym KEY=VALUE. Każdy wiersz zawiera jedną zmienną: nazwę klucza i jego wartość oddzielone znakiem równości.
Pliki .env rozwiązują fundamentalny problem współczesnego programowania: różne środowiska (lokalne, testowe, produkcyjne) wymagają zupełnie innych ustawień. URL serwera API na lokalnej maszynie to http://localhost:8080, na serwerze produkcyjnym — https://api.production.com. Jeśli te wartości są na sztywno zaszyte w kodzie aplikacji, każda kompilacja dla innego środowiska wymaga modyfikacji kodu źródłowego.
Praktyka przechowywania konfiguracji poza głównym kodem aplikacji została ustandaryzowana w manifeście The Twelve-Factor App (2011), który wskazał zmienne środowiskowe jako jedyny prawidłowy sposób konfiguracji aplikacji. Według ankiety JetBrains Developer Ecosystem (2024), ponad 67% programistów mobilnych używa plików .env w swoich projektach.
Dla programowania mobilnego .env daje dodatkową zaletę: wartości są podstawiane na etapie kompilacji przez Gradle (Android) lub xcconfig (iOS), co pozwala tworzyć osobne kompilacje dla programowania, stadowania i produkcji bez zmiany kodu źródłowego.
Szczególnie przydatny jest .env przy pracy w zespole: każdy programista tworzy własny lokalny .env z ustawieniami dla swojego środowiska (ścieżka do lokalnej bazy danych, debug-klucze API), a wspólne ustawienia są zapisywane w .env.example w repozytorium. Eliminuje to sytuację, gdy po git pull u programisty psuje się kompilacja z powodu braku zmiennej środowiskowej, o której nie wiedział. Nowy członek zespołu po prostu kopiuje .env.example do .env i wypełnia swoje lokalne wartości.
Format .env jest maksymalnie prosty: każdy wiersz to jedna zmienna w postaci KEY=VALUE. Spacje wokół znaku równości są zwykle ignorowane, ale w większości bibliotek są uważane za część wartości, więc lepiej ich unikać.
Komentarze zaczynają się od znaku # — cały wiersz po nim jest ignorowany. Puste wiersze również są pomijane. Jeśli wartość zawiera spacje, umieszcza się ją w cudzysłowie podwójnym lub pojedynczym.
# Podstawowe ustawienia środowiska
APP_NAME=MyMobileApp
APP_ENV=development
# Konfiguracja API
API_BASE_URL=http://localhost:3000/api
API_TIMEOUT=30000
# Dane poufne
DB_PASSWORD=secret_password_123
JWT_SECRET=your_jwt_secret_key
Wszystkie zmienne w .env to ciągi znaków, ale biblioteki ładujące mogą konwertować je na odpowiedni typ. Do escapowania znaków specjalnych używa się odwrotnych ukośników i cudzysłowów. Jeśli wartość zawiera znak # jako część tekstu, należy go escpować jako \#.
KEY=value lub KEY="value with spaces"PORT=8080DEBUG=trueKEY=line1\
line2DB_URL=${DB_HOST}:${DB_PORT}Podczas ładowania .env biblioteki mogą wykonywać interpolację zmiennych — podstawiać wartości jednych kluczy do innych. Na przykład zmienna DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@localhost/db rozwinie DB_USER i DB_PASS z tego samego pliku.
Sposób podłączenia .env zależy od platformy. Android używa wtyczek Gradle, iOS — plików konfiguracyjnych xcconfig, a rozwiązania wieloplatformowe takie jak Flutter — specjalistycznych bibliotek.
W Androidzie .env jest ładowany przez wtyczkę gradle-dotenv. Wtyczka odczytuje .env z katalogu głównego projektu i dodaje wartości do BuildConfig, po czym są one dostępne w kodzie Kotlin lub Java przez wygenerowane pola.
// build.gradle.kts (app level)
plugins {
id("co.uzzu.dotenv") version "4.0.0"
}
android {
buildFeatures {
buildConfig = true
}
}
kotlin {
// Dostęp w kodzie: BuildConfig.API_BASE_URL
buildConfigField("String", "API_BASE_URL",
"\"" + dotenv.get("API_BASE_URL") + "\"")
}
W iOS zmienne środowiskowe są zwykle konfigurowane przez pliki xcconfig. Do ładowania .env w Swift używa się biblioteki DotEnv lub wbudowanego mechanizmu Info.plist z niestandardowymi kluczami.
// Ładowanie .env w projekcie Swift
import DotEnv
struct AppConfig {
static func load() {
let env = DotEnv(Bundle.main)
env.load()
let apiURL = ProcessInfo.processInfo
.environment["API_BASE_URL"] ??
"https://default.api.com"
}
}
Dla Fluttera istnieje pakiet flutter_dotenv, który ładuje zmienne z .env podczas inicjalizacji aplikacji. Plik .env umieszcza się w katalogu głównym projektu, a zmienne stają się dostępne przez klasę dotenv.
// pubspec.yaml
dependencies:
flutter_dotenv: ^5.1
// main.dart — ładowanie przy starcie
import 'package:flutter_dotenv/flutter_dotenv.dart';
void main() async {
await dotenv.load(fileName: '.env');
var apiUrl = dotenv.get('API_BASE_URL');
runApp(MyApp(baseUrl: apiUrl));
}
Wszystkie trzy podejścia łączy wspólna zasada: .env jest ładowany na etapie kompilacji lub przy starcie aplikacji, wartości są cachowane i używane w kodzie przez wygenerowane stałe. Eliminuje to przedostawanie się poufnych danych do repozytorium.
Dla React Native używa się pakietu react-native-config, który na etapie kompilacji automatycznie generuje klasę BuildConfig dla Androida i stałe w Info.plist dla iOS z jednego pliku .env w katalogu głównym projektu. Jest to szczególnie wygodne dla startupów używających Expo lub bare workflow: wystarczy jeden .env na poziomie katalogu głównego, a wszystkie platformy otrzymują te same zmienne środowiskowe bez powielania konfiguracji.
Pomimo wszystkich zalet, .env nie jest pełnoprawnym rozwiązaniem do przechowywania sekretów w środowisku produkcyjnym. Zapewnia podstawowy poziom ochrony, ale przy nieprawidłowym użyciu może prowadzić do wycieku poufnych danych.
Najważniejsza zasada — .env nigdy nie powinien trafić do systemu kontroli wersji repozytorium. Plik dodaje się do .gitignore natychmiast po utworzeniu, a do repozytorium commitowany jest tylko plik-wzorzec .env.example z pustymi lub fikcyjnymi wartościami.
# .env.example — commitowany do repozytorium
APP_NAME=
APP_ENV=development
API_BASE_URL=http://localhost:3000
API_TIMEOUT=30000
# DB_PASSWORD — nie podawać nawet w przykładzie!
# JWT_SECRET — nie podawać nawet w przykładzie!
# .gitignore
# Pliki Dotenv
.env
.env*.local
Dla projektów produkcyjnych zaleca się używanie profesjonalnych rozwiązań do zarządzania sekretami. .env w produkcji jest dopuszczalny tylko jeśli plik znajduje się poza document-root serwera i ma ścisłe prawa dostępu.
Według Snyk State of Open Source Security (2024), wyciek plików .env przez repozytoria był przyczyną ponad 12% wszystkich incydentów z ujawnieniem kluczy API wśród badanych firm. Użycie osobnego menedżera sekretów pozwala zredukować to ryzyko do zera.
Dodatkową ochronę osiąga się przez wdrożenie hooków pre-commit z użyciem narzędzi takich jak husky i lint-staged, które sprawdzają, czy programista przypadkiem nie dodał .env do commita. Narzędzia takie jak git-secrets (AWS) i talisman skanują każdy commit w poszukiwaniu wzorców kluczy API, tokenów i haseł, blokując commit w przypadku wykrycia. Dla pipeline'ów CI zaleca się dodanie sprawdzania detect-secrets — automatycznego skanera, który nie przepuści pliku .env do repozytorium nawet przy błędzie programisty.
Często zadawane pytania
Nie, .env nie powinien być commitowany do Gita. Plik zawiera poufne dane i powinien być dodany do .gitignore. Zamiast niego do repozytorium trafia .env.example z szablonem wszystkich wymaganych zmiennych.
.env — rzeczywisty plik z wartościami produkcyjnymi, który nigdy nie jest commitowany. Plik .env.example zawiera te same klucze, ale z pustymi lub fikcyjnymi wartościami — jest commitowany do repozytorium jako wzorzec dla nowych programistów.
Można, ale nie jest zalecane bez dodatkowej ochrony. Jeśli .env jest używany na serwerze produkcyjnym, plik powinien znajdować się poza document-root serwera WWW z prawami dostępu 600 (tylko właściciel). Dla krytycznych projektów preferowane są menedżery sekretów.
Przez wtyczkę gradle-dotenv (co.uzzu.dotenv). Wtyczka odczytuje .env z katalogu głównego projektu i eksportuje wartości do BuildConfig. Zmienne stają się dostępne w kodzie jako BuildConfig.VARIABLE_NAME na etapie kompilacji.
Tak, wiele parserów obsługuje interpolację w formacie ${VAR_NAME}. Na przykład URL=${HOST}:${PORT} podstawi wartości HOST i PORT z tego samego pliku. Jednak ta funkcjonalność zależy od konkretnej biblioteki ładującej.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również