-
Notifications
You must be signed in to change notification settings - Fork 140
Init Script Help
This guide provides automated initialization scripts for FileBrowser using the existing admin user and API. This approach requires no special initialization mode and works across Docker, Docker Compose, Kubernetes, and bare metal deployments.
FileBrowser creates a default admin user on first startup using credentials from your configuration:
- Default username:
admin
(configurable viaauth.adminUsername
in config.yaml) - Default password:
admin
(configurable viaauth.adminPassword
in config.yaml)
The initialization scripts:
- Wait for FileBrowser to be ready (health check)
- Authenticate using the admin credentials
- Receive a JWT token
- Use the token to make authenticated API calls
- Perform setup tasks (create users, configure settings, etc.)
FileBrowser's login API accepts credentials via:
Header + Query Parameter:
curl -H "X-Password: ${PASSWORD}" \
"${FILEBROWSER_URL}/api/auth/login?username=${USERNAME}"
For TOTP/2FA:
curl -H "X-Password: ${PASSWORD}" \
-H "X-Secret: ${TOTP_CODE}" \
"${FILEBROWSER_URL}/api/auth/login?username=${USERNAME}"
Response:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... # JWT token (200 OK)
Using the Token:
curl -H "Authorization: Bearer ${TOKEN}" \
${FILEBROWSER_URL}/api/users
Create init-filebrowser.sh
:
#!/bin/bash
set -e
# Configuration from environment
FILEBROWSER_URL="${FILEBROWSER_URL:-http://localhost:8080}"
ADMIN_USERNAME="${FILEBROWSER_ADMIN_USERNAME:-admin}"
ADMIN_PASSWORD="${FILEBROWSER_ADMIN_PASSWORD:-admin}"
MAX_RETRIES="${MAX_RETRIES:-30}"
RETRY_DELAY="${RETRY_DELAY:-2}"
echo "FileBrowser initialization script started"
echo "Target URL: ${FILEBROWSER_URL}"
# Function to wait for FileBrowser to be ready
wait_for_filebrowser() {
local retries=0
echo "Waiting for FileBrowser to be ready..."
while [ $retries -lt $MAX_RETRIES ]; do
if curl -f -s "${FILEBROWSER_URL}/health" > /dev/null 2>&1; then
echo "FileBrowser is ready!"
return 0
fi
retries=$((retries + 1))
echo "Attempt ${retries}/${MAX_RETRIES} - waiting ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
echo "ERROR: FileBrowser failed to start after ${MAX_RETRIES} attempts"
return 1
}
# Function to get auth token
get_auth_token() {
local username=$1
local password=$2
echo "Logging in as ${username}..."
local response
response=$(curl -s -w "\n%{http_code}" \
-H "X-Password: ${password}" \
"${FILEBROWSER_URL}/api/auth/login?username=${username}")
local http_code=$(echo "$response" | tail -n1)
local token=$(echo "$response" | head -n-1)
if [ "$http_code" -eq 200 ] && [ -n "$token" ]; then
echo "Successfully authenticated!"
echo "$token"
return 0
else
echo "ERROR: Authentication failed (HTTP ${http_code})"
return 1
fi
}
# Function to create user
create_user() {
local token=$1
local username=$2
local password=$3
local is_admin=${4:-false}
echo "Creating user: ${username}..."
local response
response=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer ${token}" \
-H "Content-Type: application/json" \
-d "{
\"which\": [],
\"data\": {
\"username\": \"${username}\",
\"password\": \"${password}\",
\"loginMethod\": \"password\",
\"permissions\": {
\"admin\": ${is_admin},
\"modify\": true,
\"share\": true,
\"create\": true,
\"rename\": true,
\"delete\": true,
\"download\": true
},
\"scopes\": []
}
}" \
"${FILEBROWSER_URL}/api/users")
local http_code=$(echo "$response" | tail -n1)
if [ "$http_code" -eq 201 ]; then
echo "User ${username} created successfully"
return 0
elif [ "$http_code" -eq 409 ] || [ "$http_code" -eq 500 ]; then
echo "User ${username} already exists or conflicts"
return 0
else
echo "WARNING: Failed to create user ${username} (HTTP ${http_code})"
echo "$response" | head -n-1
return 1
fi
}
# Main execution
main() {
# Wait for FileBrowser to be ready
if ! wait_for_filebrowser; then
exit 1
fi
# Get authentication token
TOKEN=$(get_auth_token "$ADMIN_USERNAME" "$ADMIN_PASSWORD")
if [ -z "$TOKEN" ]; then
echo "ERROR: Failed to get authentication token"
exit 1
fi
echo ""
echo "Running initialization tasks..."
echo "Token: ${TOKEN:0:20}..."
echo ""
# Example: Create additional users
create_user "$TOKEN" "demo" "demo123" false
create_user "$TOKEN" "viewer" "viewer123" false
# Note: Settings cannot be updated via API - they must be configured
# through the config.yaml file or environment variables before startup
echo ""
echo "Initialization complete!"
}
# Run main function
main "$@"
Make the script executable:
chmod +x init-filebrowser.sh
docker-compose.yml
:
version: '3.8'
services:
filebrowser:
image: your-filebrowser:latest
ports:
- "8080:8080"
environment:
- FILEBROWSER_ADMIN_USERNAME=admin
- FILEBROWSER_ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme}
volumes:
- ./data:/data
- ./database:/database
- ./config.yaml:/config.yaml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
filebrowser-init:
image: curlimages/curl:latest
depends_on:
filebrowser:
condition: service_healthy
environment:
- FILEBROWSER_URL=http://filebrowser:8080
- FILEBROWSER_ADMIN_USERNAME=admin
- FILEBROWSER_ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme}
volumes:
- ./init-filebrowser.sh:/scripts/init-filebrowser.sh:ro
command: sh /scripts/init-filebrowser.sh
restart: "no"
volumes:
data:
database:
# Set admin password via environment
export ADMIN_PASSWORD="my-secure-password"
# Start services (init will run automatically)
docker-compose up -d
# Check init logs
docker-compose logs filebrowser-init
# Follow logs
docker-compose logs -f filebrowser-init
Create .env
file (DO NOT commit to git):
ADMIN_PASSWORD=your-secure-password-here
Update docker-compose.yml
:
version: '3.8'
services:
filebrowser:
image: your-filebrowser:latest
ports:
- "8080:8080"
env_file:
- .env
environment:
- FILEBROWSER_ADMIN_USERNAME=admin
- FILEBROWSER_ADMIN_PASSWORD=${ADMIN_PASSWORD}
volumes:
- data:/data
- database:/database
- ./config.yaml:/config.yaml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
restart: unless-stopped
filebrowser-init:
image: curlimages/curl:latest
depends_on:
filebrowser:
condition: service_healthy
env_file:
- .env
environment:
- FILEBROWSER_URL=http://filebrowser:8080
- FILEBROWSER_ADMIN_USERNAME=admin
- FILEBROWSER_ADMIN_PASSWORD=${ADMIN_PASSWORD}
volumes:
- ./init-filebrowser.sh:/scripts/init-filebrowser.sh:ro
command: sh /scripts/init-filebrowser.sh
restart: "no"
volumes:
data:
driver: local
database:
driver: local
Create filebrowser-init-job.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: filebrowser-secrets
namespace: default
type: Opaque
stringData:
admin-username: admin
admin-password: changeme-in-production
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebrowser-init-script
namespace: default
data:
init.sh: |
#!/bin/sh
set -e
echo "Waiting for FileBrowser to be ready..."
RETRIES=0
MAX_RETRIES=30
until curl -f -s "${FILEBROWSER_URL}/health" > /dev/null 2>&1; do
RETRIES=$((RETRIES + 1))
if [ $RETRIES -ge $MAX_RETRIES ]; then
echo "ERROR: FileBrowser not ready after ${MAX_RETRIES} attempts"
exit 1
fi
echo "Waiting... (${RETRIES}/${MAX_RETRIES})"
sleep 2
done
echo "FileBrowser is ready!"
echo "Getting auth token..."
TOKEN=$(curl -s -H "X-Password: ${ADMIN_PASSWORD}" \
"${FILEBROWSER_URL}/api/auth/login?username=${ADMIN_USERNAME}")
if [ -z "$TOKEN" ]; then
echo "ERROR: Failed to authenticate"
exit 1
fi
echo "Authenticated successfully!"
echo "Token: ${TOKEN:0:20}..."
# Create demo user
echo "Creating demo user..."
HTTP_CODE=$(curl -s -w "%{http_code}" -o /dev/null \
-X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"which":[],"data":{"username":"demo","password":"demo123","loginMethod":"password","permissions":{"admin":false,"modify":true,"share":true,"create":true,"rename":true,"delete":true,"download":true},"scopes":[]}}' \
"${FILEBROWSER_URL}/api/users")
if [ "$HTTP_CODE" = "201" ]; then
echo "Demo user created successfully"
elif [ "$HTTP_CODE" = "409" ] || [ "$HTTP_CODE" = "500" ]; then
echo "Demo user already exists"
else
echo "WARNING: Failed to create demo user (HTTP ${HTTP_CODE})"
fi
echo "Initialization complete!"
---
apiVersion: batch/v1
kind: Job
metadata:
name: filebrowser-init
namespace: default
spec:
ttlSecondsAfterFinished: 300
backoffLimit: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: init
image: curlimages/curl:latest
command: ["/bin/sh", "/scripts/init.sh"]
env:
- name: FILEBROWSER_URL
value: "http://filebrowser.default.svc.cluster.local"
- name: ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: filebrowser-secrets
key: admin-username
- name: ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: filebrowser-secrets
key: admin-password
volumeMounts:
- name: init-script
mountPath: /scripts
volumes:
- name: init-script
configMap:
name: filebrowser-init-script
defaultMode: 0755
Create filebrowser-deployment.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: filebrowser-secrets
namespace: default
type: Opaque
stringData:
admin-username: admin
admin-password: changeme-in-production
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebrowser-config
namespace: default
data:
config.yaml: |
server:
port: 8080
database: /database/database.db
sources:
- name: files
path: /data
auth:
adminUsername: admin
adminPassword: changeme
userDefaults:
permissions:
modify: false
share: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: filebrowser
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: filebrowser
template:
metadata:
labels:
app: filebrowser
spec:
containers:
- name: filebrowser
image: your-filebrowser:latest
ports:
- containerPort: 8080
name: http
env:
- name: FILEBROWSER_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: filebrowser-secrets
key: admin-username
- name: FILEBROWSER_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: filebrowser-secrets
key: admin-password
volumeMounts:
- name: data
mountPath: /data
- name: database
mountPath: /database
- name: config
mountPath: /config
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: filebrowser-data
- name: database
persistentVolumeClaim:
claimName: filebrowser-database
- name: config
configMap:
name: filebrowser-config
---
apiVersion: v1
kind: Service
metadata:
name: filebrowser
namespace: default
spec:
selector:
app: filebrowser
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: filebrowser-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: filebrowser-database
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# Create namespace (optional)
kubectl create namespace filebrowser
# Update the secret with your password
kubectl create secret generic filebrowser-secrets \
--from-literal=admin-username=admin \
--from-literal=admin-password=your-secure-password \
-n filebrowser
# Deploy FileBrowser
kubectl apply -f filebrowser-deployment.yaml -n filebrowser
# Wait for deployment
kubectl wait --for=condition=ready pod -l app=filebrowser -n filebrowser --timeout=60s
# Run init job
kubectl apply -f filebrowser-init-job.yaml -n filebrowser
# Check init job status
kubectl get jobs -n filebrowser
kubectl logs job/filebrowser-init -n filebrowser
# Access FileBrowser (port-forward for testing)
kubectl port-forward svc/filebrowser 8080:80 -n filebrowser
Create /etc/systemd/system/filebrowser.service
:
[Unit]
Description=FileBrowser Service
After=network.target
[Service]
Type=simple
User=filebrowser
Group=filebrowser
Environment="FILEBROWSER_ADMIN_USERNAME=admin"
Environment="FILEBROWSER_ADMIN_PASSWORD=changeme"
EnvironmentFile=-/etc/filebrowser/env
WorkingDirectory=/opt/filebrowser
ExecStart=/opt/filebrowser/filebrowser
ExecStartPost=/bin/sleep 5
ExecStartPost=/opt/filebrowser/init-filebrowser.sh
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Create /etc/filebrowser/env
:
FILEBROWSER_ADMIN_USERNAME=admin
FILEBROWSER_ADMIN_PASSWORD=your-secure-password
FILEBROWSER_URL=http://localhost:8080
# Create user
sudo useradd -r -s /bin/false filebrowser
# Create directories
sudo mkdir -p /opt/filebrowser
sudo mkdir -p /etc/filebrowser
sudo mkdir -p /var/lib/filebrowser
# Copy files
sudo cp filebrowser /opt/filebrowser/
sudo cp init-filebrowser.sh /opt/filebrowser/
sudo cp config.yaml /opt/filebrowser/
sudo chmod +x /opt/filebrowser/filebrowser
sudo chmod +x /opt/filebrowser/init-filebrowser.sh
# Set permissions
sudo chown -R filebrowser:filebrowser /opt/filebrowser
sudo chown -R filebrowser:filebrowser /var/lib/filebrowser
sudo chmod 600 /etc/filebrowser/env
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable filebrowser
sudo systemctl start filebrowser
# Check status
sudo systemctl status filebrowser
sudo journalctl -u filebrowser -f
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"which": [],
"data": {
"username": "newuser",
"password": "password123",
"loginMethod": "password",
"permissions": {
"admin": false,
"modify": true,
"share": true,
"create": true,
"rename": true,
"delete": true,
"download": true
},
"scopes": []
}
}' \
http://localhost:8080/api/users
curl -H "Authorization: Bearer ${TOKEN}" \
http://localhost:8080/api/users
# Get by ID
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/users?id=1"
# Get self
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/users?id=self"
curl -X PUT \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"which": ["password"],
"data": {
"password": "newpassword123"
}
}' \
"http://localhost:8080/api/users?id=1"
curl -X DELETE \
-H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/users?id=1"
Note: Settings cannot be updated via API. They must be configured in config.yaml
before starting FileBrowser.
# Get all settings
curl -H "Authorization: Bearer ${TOKEN}" \
http://localhost:8080/api/settings
# Get specific property
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/settings?property=userDefaults"
# Available properties: userDefaults, frontend, auth, server, sources
# Get full configuration with comments
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/settings/config?full=true&comments=true"
# Get only non-default values
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/settings/config?full=false&comments=false"
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"path": "/shared-folder",
"source": "files",
"expire": "2025-12-31T23:59:59Z",
"password": "sharepass123",
"downloadsLimit": 10,
"allowUpload": false,
"disableFileViewer": false,
"disableAnonymous": false,
"maxBandwidth": 0,
"title": "My Share",
"description": "Shared files"
}' \
http://localhost:8080/api/share
# List all shares (admin) or own shares (non-admin)
curl -H "Authorization: Bearer ${TOKEN}" \
http://localhost:8080/api/shares
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/share?path=/shared-folder&source=files"
curl -X DELETE \
-H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8080/api/share?hash=abc123def456"
-
POST /api/auth/login
- Login with username and password -
POST /api/auth/logout
- Logout current user -
POST /api/auth/signup
- Register new user (if signup enabled) -
POST /api/auth/renew
- Renew authentication token -
POST /api/auth/otp/generate
- Generate TOTP secret for 2FA -
POST /api/auth/otp/verify
- Verify TOTP code -
PUT /api/auth/token
- Create API key -
GET /api/auth/token
- Create API key -
DELETE /api/auth/token
- Delete API key -
GET /api/auth/tokens
- List API keys -
GET /api/auth/oidc/callback
- OIDC callback -
GET /api/auth/oidc/login
- OIDC login
-
GET /api/users
- List users or get specific user -
POST /api/users
- Create new user (admin only) -
PUT /api/users
- Update user -
DELETE /api/users
- Delete user (admin only)
-
GET /api/resources
- Get file/folder information -
DELETE /api/resources
- Delete file/folder -
POST /api/resources
- Create file/folder or upload -
PUT /api/resources
- Update/move file/folder -
PATCH /api/resources
- Partial update file/folder -
GET /api/raw
- Download raw file -
GET /api/preview
- Get file preview -
GET /api/media/subtitles
- Get media subtitles
-
GET /api/shares
- List all shares -
GET /api/share
- Get share by path -
POST /api/share
- Create share -
DELETE /api/share
- Delete share -
GET /api/share/direct
- Create direct download link
-
GET /api/settings
- Get system settings -
GET /api/settings/config
- Get settings as YAML
-
GET /api/access
- Get access rules -
POST /api/access
- Create access rule -
DELETE /api/access
- Delete access rule -
GET /api/access/groups
- Get access groups -
POST /api/access/group
- Create access group -
DELETE /api/access/group
- Delete access group
-
GET /api/search
- Search files -
GET /api/events
- Server-Sent Events stream -
GET /api/jobs/{action}/{target}
- Get job status -
GET /health
- Health check endpoint
-
GET /public/api/raw
- Public raw file download -
GET /public/api/preview
- Public file preview -
GET /public/api/resources
- Public resource info -
POST /public/api/resources
- Public upload (if share allows)
- Never hardcode credentials - Use environment variables or secrets management
- Rotate admin password - Change default password immediately after first login
- Use HTTPS in production - Configure TLS certificates
- Limit network exposure - Use firewalls and network policies
- Audit API access - Monitor logs for unauthorized access attempts
- Configure settings in config.yaml - Settings cannot be changed via API, so configure them properly before deployment
- Wait for health checks - Always verify service is ready before init
- Make scripts idempotent - Handle "already exists" errors gracefully
- Set timeouts - Don't wait indefinitely for service startup
- Use retries - Network issues can cause transient failures
- Log all actions - Helps with debugging and compliance
- Version control init scripts - Track changes over time
- Document custom operations - Add comments for complex logic
- Use functions - Make scripts modular and reusable
- Test in staging - Verify scripts before production deployment
- Keep scripts simple - Complex logic belongs in application code
- Update config.yaml for settings - Remember that settings are read-only via API
Since settings cannot be updated via the API, you must configure FileBrowser through the config.yaml
file. Here's an example:
server:
port: 8080
baseURL: /
database: /database/database.db
log: stdout
sources:
- name: files
path: /data
auth:
adminUsername: admin
adminPassword: changeme
key: random-secret-key
tokenExpirationHours: 168
methods:
passwordAuth:
enabled: true
signup: false
enforcedOtp: false
userDefaults:
permissions:
admin: false
modify: true
share: true
create: true
rename: true
delete: true
download: true
scopes: []
frontend:
name: "FileBrowser"
disableExternal: false
files: []
- Uses existing authentication API
- No special init mode needed
- Works with current codebase as-is
- Credentials passed via environment variables
- No secrets in process list or command history
- Compatible with secrets management systems
- Works with Kubernetes Secrets
- Supports Docker Compose secrets
- Compatible with HashiCorp Vault, AWS Secrets Manager, etc.
- Works across all deployment types
- Easy to extend with additional setup tasks
- Scripts can be customized per environment
- Similar to database migration scripts
- Standard init container pattern
- DevOps teams will recognize the approach
Problem: Script cannot reach FileBrowser service
Solutions:
# Check service is running
docker-compose ps
kubectl get pods
# Check health endpoint
curl http://localhost:8080/health
# Verify network connectivity
ping filebrowser
nslookup filebrowser.default.svc.cluster.local
# Check firewall rules
iptables -L
kubectl get networkpolicies
Problem: Cannot get token from login API
Solutions:
# Verify credentials are correct
echo "Username: ${FILEBROWSER_ADMIN_USERNAME}"
echo "Password: ${FILEBROWSER_ADMIN_PASSWORD}"
# Test login manually
curl -v -H "X-Password: admin" \
"http://localhost:8080/api/auth/login?username=admin"
# Check logs for authentication errors
docker-compose logs filebrowser
kubectl logs deployment/filebrowser
Problem: Kubernetes Job retries unnecessarily
Solutions:
- Ensure script exits with code 0 on success
- Set
backoffLimit
in Job spec - Make script idempotent (handle existing resources)
- Add
ttlSecondsAfterFinished
to clean up completed jobs
Problem: Script cannot execute or access files
Solutions:
# Make script executable
chmod +x init-filebrowser.sh
# Check file ownership
ls -la init-filebrowser.sh
# Verify volume mounts in Docker
docker-compose exec filebrowser-init ls -la /scripts
# Check SecurityContext in Kubernetes
kubectl describe pod <pod-name>
Problem: Trying to update settings via API
Solution:
Settings are read-only via the API. You must update the config.yaml
file and restart FileBrowser for changes to take effect. This is by design to ensure configuration consistency.