diff --git a/.github/workflows/build_bench.yml b/.github/workflows/build_bench.yml index e5a61141..1835f1c3 100644 --- a/.github/workflows/build_bench.yml +++ b/.github/workflows/build_bench.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup QEMU uses: docker/setup-qemu-action@v3 @@ -38,7 +38,7 @@ jobs: run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV" - name: Build and test - uses: docker/bake-action@v6.8.0 + uses: docker/bake-action@v6.9.0 with: source: . targets: bench-test @@ -52,7 +52,7 @@ jobs: - name: Push if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} - uses: docker/bake-action@v6.8.0 + uses: docker/bake-action@v6.9.0 with: targets: bench push: true diff --git a/.github/workflows/build_develop.yml b/.github/workflows/build_develop.yml index e37c5858..6103fd5f 100644 --- a/.github/workflows/build_develop.yml +++ b/.github/workflows/build_develop.yml @@ -27,7 +27,7 @@ jobs: version: develop push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} python_version: 3.11.6 - node_version: 18.18.2 + node_version: 20.19.2 secrets: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/build_stable.yml b/.github/workflows/build_stable.yml index 275c22ac..6ddbb0ea 100644 --- a/.github/workflows/build_stable.yml +++ b/.github/workflows/build_stable.yml @@ -62,10 +62,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.10" diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml index 3694ec00..d17dbf9e 100644 --- a/.github/workflows/docker-build-push.yml +++ b/.github/workflows/docker-build-push.yml @@ -43,7 +43,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup QEMU uses: docker/setup-qemu-action@v3 @@ -66,7 +66,7 @@ jobs: echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV" - name: Build - uses: docker/bake-action@v6.8.0 + uses: docker/bake-action@v6.9.0 with: source: . push: true @@ -74,7 +74,7 @@ jobs: REGISTRY_USER: localhost:5000/frappe - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.10" @@ -95,7 +95,7 @@ jobs: - name: Push if: ${{ inputs.push }} - uses: docker/bake-action@v6.8.0 + uses: docker/bake-action@v6.9.0 with: push: true set: "*.platform=linux/amd64,linux/arm64" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ce4ab6ee..6f72e86b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,16 +13,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.10.6" # For shfmt pre-commit hook - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: "^1.14" diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 5612c735..29a5b5af 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Update pre-commit hooks uses: vrslev/pre-commit-autoupdate@v1.0.0 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 024ade1c..8849bfea 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -9,7 +9,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: This issue has been automatically marked as stale. You have a week to explain why you believe this is an error. diff --git a/README.md b/README.md index 08ad3352..41bc4870 100644 --- a/README.md +++ b/README.md @@ -54,544 +54,44 @@ Click below to instantly spin up a Frappe/ERPNext instance in your browser: Try in PWD -### Option 2: Local Development Setup +### Try on your Dev environment -1. **Clone the repository:** +First clone the repo: -```bash +```sh git clone https://github.com/frappe/frappe_docker cd frappe_docker ``` -2. **Start the containers:** +Then run: `docker compose -f pwd.yml up -d` -```bash -docker compose -f pwd.yml up -d -``` +### To run on ARM64 architecture follow this instructions -3. **Wait for initialization** (approximately 5 minutes) +After you clone the repo and `cd frappe_docker`, run this command to build multi-architecture images specifically for ARM64. -```bash -# Monitor the setup progress -docker compose -f pwd.yml logs -f create-site -``` +`docker buildx bake --no-cache --set "*.platform=linux/arm64"` -4. **Access ERPNext:** +and then -- URL: `http://localhost:8080` -- Username: `Administrator` -- Password: `admin` +- add `platform: linux/arm64` to all services in the `pwd.yml` +- replace the current specified versions of erpnext image on `pwd.yml` with `:latest` -### Option 3: Production Setup +Then run: `docker compose -f pwd.yml up -d` -1. **Clone and configure:** +## Final steps -```bash -git clone https://github.com/frappe/frappe_docker -cd frappe_docker -cp example.env .env -# Edit .env file with your configuration -``` +Wait for 5 minutes for ERPNext site to be created or check `create-site` container logs before opening browser on port 8080. (username: `Administrator`, password: `admin`) -2. **Deploy with production settings:** +If you ran in a Dev Docker environment, to view container logs: `docker compose -f pwd.yml logs -f create-site`. Don't worry about some of the initial error messages, some services take a while to become ready, and then they go away. -```bash -docker compose up -d -``` +# Documentation -### ARM64 Architecture Support +### [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions) -For ARM64 systems (Apple Silicon, Raspberry Pi): +### [Production](#production) -```bash -# Build ARM64 images -docker buildx bake --no-cache --set "*.platform=linux/arm64" - -# Modify pwd.yml to add platform: linux/arm64 to all services -# Then deploy -docker compose -f pwd.yml up -d -``` - -## Bench Wrapper Script - -To simplify working with bench commands, we provide a convenient wrapper script `bench.sh` that eliminates the need to type `docker compose exec backend bench` repeatedly. - -### Setup - -```bash -# Make the script executable (one-time setup) -chmod +x bench.sh -``` - -### Usage - -The script automatically detects which compose file you're using and provides a simpler interface: - -```bash -# Instead of: docker compose exec backend bench new-site mysite.local -./bench.sh new-site mysite.local - -# Instead of: docker compose exec backend bench --site mysite.local migrate -./bench.sh --site mysite.local migrate - -# With specific project name -./bench.sh -p erpnext-prod --site production.local backup - -# Get help -./bench.sh --help -``` - -### Features - -- **Auto-detection**: Automatically finds and uses the correct compose file (pwd.yml or compose.yaml) -- **Project support**: Use `-p` flag for specific docker-compose projects -- **Full compatibility**: Supports all bench commands and arguments -- **Error handling**: Checks if containers are running before executing commands -- **Help system**: Built-in help with common command examples - -## Deployment Options - -### Development Environment - -Perfect for developers working on Frappe apps: - -```bash -# Setup development environment with VSCode integration -cp -R devcontainer-example .devcontainer -cp -R development/vscode-example development/.vscode - -# Open in VSCode with Dev Containers extension -code . -# Then: "Reopen in Container" -``` - -**Features:** - -- Hot-reload support -- Debugging capabilities -- Pre-configured VSCode settings -- Multiple Python/Node versions - -### Production Environment - -For production deployments: - -```bash -# Using standard compose file -docker compose up -d - -# With external database/Redis -# Configure DB_HOST, REDIS_CACHE, REDIS_QUEUE in .env -docker compose up -d -``` - -**Features:** - -- Auto-restart on failure -- Health checks -- Log rotation -- Backup capabilities - -### Single Server Setup - -Simplified setup for single server deployments: - -```bash -docker compose -f pwd.yml up -d -``` - -This creates a complete ERPNext instance with all required services. - -## Architecture - -The Frappe Docker setup consists of multiple interconnected services: - -### Core Services - -| Service | Purpose | Port | -| --------------- | --------------------------- | ---- | -| **backend** | Gunicorn application server | 8000 | -| **frontend** | Nginx reverse proxy | 8080 | -| **websocket** | Socket.io real-time server | 9000 | -| **scheduler** | Background job scheduler | - | -| **queue-short** | Short-running job worker | - | -| **queue-long** | Long-running job worker | - | - -### Supporting Services - -| Service | Purpose | Default | -| --------------- | --------------------------- | ------------ | -| **db** | MariaDB/PostgreSQL database | MariaDB 10.6 | -| **redis-cache** | Redis cache server | Redis 6.2 | -| **redis-queue** | Redis queue server | Redis 6.2 | - -### Service Flow - -``` -User Request → Nginx (frontend) → Gunicorn (backend) → Database - ↓ ↓ - WebSocket Redis Cache - ↓ - Job Queues → Workers -``` - -## Common Tasks - -### Using the Bench Wrapper Script - -For convenience, we provide a `bench.sh` wrapper script that simplifies running bench commands from your host machine. - -```bash -# Make the script executable (first time only) -chmod +x bench.sh - -# Create a new site -./bench.sh new-site mysite.local --admin-password=admin --install-app erpnext - -# Run migrations -./bench.sh --site mysite.local migrate - -# Backup a site -./bench.sh --site mysite.local backup - -# Get help -./bench.sh --help -``` - -The script automatically detects which compose file you're using and handles the docker compose execution for you. - -### Site Management - -#### Create a new site - -Using wrapper script: - -```bash -./bench.sh new-site mysite.local \ - --mariadb-user-host-login-scope=% \ - --db-root-password=admin \ - --admin-password=admin \ - --install-app erpnext -``` - -Or directly with docker compose: - -```bash -docker compose exec backend bench new-site mysite.local \ - --mariadb-user-host-login-scope=% \ - --db-root-password=admin \ - --admin-password=admin \ - --install-app erpnext -``` - -#### List all sites - -Using wrapper script: - -```bash -./bench.sh --site all list-apps -``` - -Or directly: - -```bash -docker compose exec backend bench --site all list-apps -``` - -#### Migrate a site - -Using wrapper script: - -```bash -./bench.sh --site mysite.local migrate -``` - -Or directly: - -```bash -docker compose exec backend bench --site mysite.local migrate -``` - -#### Backup a site - -Using wrapper script: - -```bash -./bench.sh --site mysite.local backup -``` - -Or directly: - -```bash -docker compose exec backend bench --site mysite.local backup -``` - -### App Management - -#### Install an app - -Using wrapper script: - -```bash -# Get the app -./bench.sh get-app https://github.com/frappe/app_name - -# Install on site -./bench.sh --site mysite.local install-app app_name -``` - -Or directly: - -```bash -# Get the app -docker compose exec backend bench get-app https://github.com/frappe/app_name - -# Install on site -docker compose exec backend bench --site mysite.local install-app app_name -``` - -#### Update apps - -Using wrapper script: - -```bash -./bench.sh update --pull --apps -``` - -Or directly: - -```bash -docker compose exec backend bench update --pull --apps -``` - -### Database Operations - -#### Access MariaDB console - -```bash -docker compose exec db mysql -uroot -padmin -``` - -#### Import database - -Using wrapper script: - -```bash -./bench.sh --site mysite.local --force restore path/to/backup.sql.gz -``` - -Or directly: - -```bash -docker compose exec backend bench --site mysite.local \ - --force restore path/to/backup.sql.gz -``` - -### Monitoring & Logs - -#### View all logs - -```bash -docker compose logs -f -``` - -#### View specific service logs - -```bash -docker compose logs -f backend -docker compose logs -f frontend -``` - -#### Check service health - -```bash -docker compose ps -docker compose exec backend healthcheck.sh -``` - -### Security & SSL - -#### Setup Let's Encrypt SSL - -```bash -# Configure in .env -LETSENCRYPT_EMAIL=your@email.com -SITES=`yourdomain.com` - -# Deploy with Traefik -docker compose -f compose.yaml -f overrides/compose.https.yaml up -d -``` - -## Configuration - -### Environment Variables - -Create a `.env` file from the example: - -```bash -cp example.env .env -``` - -Key configuration options: - -| Variable | Description | Default | -| ------------------------- | ------------------------- | ---------- | -| `ERPNEXT_VERSION` | ERPNext version to deploy | `v15.69.2` | -| `DB_PASSWORD` | Database root password | `123` | -| `DB_HOST` | External database host | - | -| `REDIS_CACHE` | External Redis cache URL | - | -| `REDIS_QUEUE` | External Redis queue URL | - | -| `HTTP_PUBLISH_PORT` | HTTP port | `8080` | -| `FRAPPE_SITE_NAME_HEADER` | Site resolution header | `$$host` | - -### Custom Apps Configuration - -To include custom apps, create `apps.json`: - -```json -[ - { - "url": "https://github.com/frappe/erpnext", - "branch": "version-15" - }, - { - "url": "https://github.com/yourusername/custom-app", - "branch": "main" - } -] -``` - -Build custom image: - -```bash -export APPS_JSON_BASE64=$(base64 -i apps.json) -docker buildx bake -f docker-bake.hcl custom -``` - -### Multi-tenancy Setup - -For hosting multiple sites on different ports: - -```yaml -# compose.override.yml -services: - frontend: - ports: - - "8081:8080" # Site 1 - - "8082:8080" # Site 2 -``` - -## Advanced Features - -### Backup Automation - -Setup automated backups to S3: - -```bash -# Create backup script -docker compose exec backend push_backup.py \ - --site-name mysite.local \ - --bucket my-bucket \ - --region-name us-east-1 \ - --endpoint-url https://s3.amazonaws.com \ - --aws-access-key-id KEY \ - --aws-secret-access-key SECRET -``` - -### Custom Build Arguments - -Build with specific versions: - -```bash -docker buildx bake \ - --set "*.args.FRAPPE_VERSION=v15.0.0" \ - --set "*.args.ERPNEXT_VERSION=v15.0.0" \ - --set "*.args.PYTHON_VERSION=3.11.6" \ - --set "*.args.NODE_VERSION=18.18.2" -``` - -### Development with Bench - -Inside development container: - -```bash -# Create new bench -bench init --skip-redis-config-generation frappe-bench -cd frappe-bench - -# Start bench -bench start - -# Create new app -bench new-app my_custom_app - -# Install app -bench --site mysite.local install-app my_custom_app -``` - -## Troubleshooting - -### Container won't start - -```bash -# Check logs -docker compose logs backend - -# Verify configuration -docker compose config - -# Restart services -docker compose restart -``` - -### Site not accessible - -```bash -# Check site configuration (using wrapper script) -./bench.sh --site mysite.local show-config - -# Or directly -docker compose exec backend bench --site mysite.local show-config - -# Verify nginx is running -docker compose exec frontend nginx -t -``` - -### Database connection issues - -```bash -# Test database connection (using wrapper script) -./bench.sh --site mysite.local mariadb - -# Or directly -docker compose exec backend bench --site mysite.local mariadb - -# Check database logs -docker compose logs db -``` - -### Permission errors - -```bash -# Fix permissions -docker compose exec backend chown -R frappe:frappe /home/frappe/frappe-bench -``` - -### Reset admin password - -```bash -# Using wrapper script -./bench.sh --site mysite.local set-admin-password newpassword - -# Or directly -docker compose exec backend bench --site mysite.local set-admin-password newpassword -``` - -## Documentation - -### Essential Guides - -- [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions) +- [List of containers](docs/list-of-containers.md) +- [Single Compose Setup](docs/single-compose-setup.md) - [Environment Variables](docs/environment-variables.md) - [Site Operations](docs/site-operations.md) - [Custom Apps](docs/custom-apps.md) diff --git a/devcontainer-example/docker-compose.yml b/devcontainer-example/docker-compose.yml index 3df5c417..0a614cbf 100644 --- a/devcontainer-example/docker-compose.yml +++ b/devcontainer-example/docker-compose.yml @@ -2,7 +2,6 @@ version: "3.7" services: mariadb: image: docker.io/mariadb:10.6 - platform: linux/amd64 command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci @@ -15,7 +14,7 @@ services: # Enable PostgreSQL only if you use it, see development/README.md for more information. # postgresql: - # image: postgres:11.8 + # image: postgres:14 # environment: # POSTGRES_PASSWORD: 123 # volumes: @@ -38,15 +37,14 @@ services: redis-cache: image: docker.io/redis:alpine - platform: linux/amd64 redis-queue: image: docker.io/redis:alpine - platform: linux/amd64 frappe: image: docker.io/frappe/bench:latest - platform: linux/amd64 + # If you want to build the current bench image the Containerfile is in this Repo. + # build: ../images/bench command: sleep infinity environment: - SHELL=/bin/bash diff --git a/docker-bake.hcl b/docker-bake.hcl index 3304ce40..9d3ccd00 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -9,7 +9,7 @@ variable PYTHON_VERSION { default = "3.11.6" } variable NODE_VERSION { - default = "18.18.2" + default = "20.19.2" } variable "FRAPPE_VERSION" { diff --git a/docs/environment-variables.md b/docs/environment-variables.md index b1a44d9f..616b06fa 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -25,6 +25,10 @@ Frappe framework release. You can find all releases [here](https://github.com/fr Password for MariaDB (or Postgres) database. +### `DB_PASSWORD_SECRETS_FILE` + +Path to the db_password.txt file. Set only if you use docker secrets for the database password (use `overrides/compose.mariadb-secrets.yaml`) + ### `DB_HOST` Hostname for MariaDB (or Postgres) database. Set only if external service for database is used or the container can not be reached by its service name (db) by other containers. diff --git a/docs/single-server-example.md b/docs/single-server-example.md index ca61cda1..0bb83798 100644 --- a/docs/single-server-example.md +++ b/docs/single-server-example.md @@ -65,7 +65,7 @@ Create a file called `traefik.env` in `~/gitops` ```shell echo 'TRAEFIK_DOMAIN=traefik.example.com' > ~/gitops/traefik.env echo 'EMAIL=admin@example.com' >> ~/gitops/traefik.env -echo 'HASHED_PASSWORD='$(openssl passwd -apr1 changeit | sed -e s/\\$/\\$\\$/g) >> ~/gitops/traefik.env +echo "HASHED_PASSWORD='$(openssl passwd -apr1 changeit)'" >> ~/gitops/traefik.env ``` Note: diff --git a/example.env b/example.env index d66de939..103f761b 100644 --- a/example.env +++ b/example.env @@ -1,9 +1,12 @@ # Reference: https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md -ERPNEXT_VERSION=v15.69.2 +ERPNEXT_VERSION=v15.80.0 DB_PASSWORD=123 +#Only if you use docker secrets for the db password +DB_PASSWORD_SECRETS_FILE= + # Only if you use external database DB_HOST= DB_PORT= diff --git a/overrides/compose.mariadb-secrets.yaml b/overrides/compose.mariadb-secrets.yaml new file mode 100644 index 00000000..a8172431 --- /dev/null +++ b/overrides/compose.mariadb-secrets.yaml @@ -0,0 +1,13 @@ +services: + db: + environment: + MYSQL_ROOT_PASSWORD: !reset null + MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password + healthcheck: + test: mysqladmin ping -h localhost --password="$(cat /run/secrets/db_password)" + secrets: + - db_password + +secrets: + db_password: + file: ${DB_PASSWORD_SECRETS_FILE:?No db secret file set} diff --git a/overrides/compose.mariadb.yaml b/overrides/compose.mariadb.yaml index 1d6e55c6..ebce5038 100644 --- a/overrides/compose.mariadb.yaml +++ b/overrides/compose.mariadb.yaml @@ -10,7 +10,7 @@ services: db: image: mariadb:10.6 healthcheck: - test: mysqladmin ping -h localhost --password=${DB_PASSWORD} + test: mysqladmin ping -h localhost --password=${DB_PASSWORD:-123} interval: 1s retries: 20 restart: unless-stopped @@ -20,7 +20,7 @@ services: - --skip-character-set-client-handshake - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 environment: - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:?No db password set} + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123} volumes: - db-data:/var/lib/mysql diff --git a/pwd.yml b/pwd.yml index 603263cb..bd0e52c2 100644 --- a/pwd.yml +++ b/pwd.yml @@ -2,7 +2,7 @@ version: "3" services: backend: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -18,7 +18,7 @@ services: MARIADB_ROOT_PASSWORD: admin configurator: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -47,7 +47,7 @@ services: - logs:/home/frappe/frappe-bench/logs create-site: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -102,7 +102,7 @@ services: - db-data:/var/lib/mysql frontend: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network depends_on: @@ -128,7 +128,7 @@ services: - "8080:8080" queue-long: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -144,7 +144,7 @@ services: - logs:/home/frappe/frappe-bench/logs queue-short: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -178,7 +178,7 @@ services: condition: on-failure scheduler: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: @@ -192,7 +192,7 @@ services: - logs:/home/frappe/frappe-bench/logs websocket: - image: frappe/erpnext:v15.69.2 + image: frappe/erpnext:v15.80.0 networks: - frappe_network deploy: diff --git a/requirements-test.txt b/requirements-test.txt index 8e0e8841..9471b3d9 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1 +1 @@ -pytest==8.4.1 +pytest==8.4.2 diff --git a/tests/conftest.py b/tests/conftest.py index 55838c6b..c6ff166a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -151,6 +151,7 @@ def s3_service(python_path: str, compose: Compose): subprocess.check_call(cmd) compose("cp", "tests/_create_bucket.py", "backend:/tmp") + compose.exec("backend", "bench", "pip", "install", "boto3~=1.34.143") compose.exec( "-e", f"S3_ACCESS_KEY={access_key}",