Best Practices#
Guidelines for using GeoServer CLI effectively and safely.
Configuration Management#
Use Environment-Specific Configs#
Create separate configurations for each environment:
1
2
3
4
5
| configs/
├── default.config.toml # Template
├── dev.config.toml # Development
├── staging.config.toml # Staging
└── prod.config.toml # Production
|
Switch between them easily:
1
| export GEOSRVCLI_CONFIG=prod
|
Never Commit Passwords#
Keep sensitive data out of version control:
1
2
3
4
| # .gitignore
configs/*.config.toml
!configs/default.config.toml # Template only
.envrc
|
Use environment variables for passwords:
1
2
| # .envrc (gitignored)
export GEOSRVCLI_PASSWORD=$(vault read -field=password secret/geoserver)
|
Use direnv for Local Development#
Install direnv and create .envrc:
1
2
3
4
5
| # .envrc
export GEOSRVCLI_CONFIG=dev
export GEOSRVCLI_DEFAULT_WORKSPACE=dev_workspace
export GEOSRVCLI_PASSWORD=dev_password
PATH_add ./bin
|
Then:
Validate Before Deploy#
Always validate configuration before production use:
1
2
3
4
5
6
7
8
| # Validate config file
./geoserver-cli config validate --config prod
# Show resolved configuration
./geoserver-cli config show --config prod --redact
# Test connection
./geoserver-cli workspace list --config prod
|
Publishing Workflows#
Use Dry Run First#
Preview changes before executing:
1
2
3
4
5
| # See what would be published
./geoserver-cli publish postgis --all -w prod --dry-run
# Review output, then execute
./geoserver-cli publish postgis --all -w prod
|
Publish in Stages#
For production, break publishing into stages:
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/bin/bash
# Stage 1: Create workspace
./geoserver-cli workspace create production
# Stage 2: Publish core layers
./geoserver-cli publish postgis --layers roads,buildings -w production
# Stage 3: Apply styles
./geoserver-cli style create road_style --sld @styles/road.sld
./geoserver-cli layer update roads --default-style road_style
# Stage 4: Enable and advertise
./geoserver-cli layer update roads --enabled true --advertised true
|
Use Filters for Bulk Publishing#
Instead of publishing everything, use filters:
1
2
3
4
| # Publish only production tables
./geoserver-cli publish postgis --all -w prod \
--include "^prod_" \
--exclude "_staging$"
|
Leverage Concurrency Wisely#
Use concurrency but don’t overwhelm the server:
1
2
3
4
5
| # Good: Moderate concurrency
./geoserver-cli publish postgis --all -w prod --concurrency 4
# Bad: Too much concurrency may overwhelm server
./geoserver-cli publish postgis --all -w prod --concurrency 32
|
Handle Failures Gracefully#
Use idempotent commands and error handling:
1
2
3
4
5
6
7
8
9
10
11
| #!/bin/bash
set -e
# Idempotent: Skip existing layers
./geoserver-cli publish postgis --all -w prod || {
echo "Publishing failed. Check logs."
exit 1
}
# Continue even if some operations fail
./geoserver-cli layer update roads --enabled true || echo "Failed to update roads"
|
Workspace Organization#
Use Meaningful Workspace Names#
Choose descriptive, environment-specific names:
1
2
3
4
5
6
7
8
| # Good
./geoserver-cli workspace create production
./geoserver-cli workspace create staging_v2
./geoserver-cli workspace create dev_john
# Bad
./geoserver-cli workspace create ws1
./geoserver-cli workspace create test
|
Set Default Workspace#
Set a default to avoid repetition:
1
2
3
4
5
6
| # In .envrc or environment
export GEOSRVCLI_DEFAULT_WORKSPACE=production
# Now all commands use this workspace
./geoserver-cli store list
./geoserver-cli publish postgis --all
|
Keep Workspaces Focused#
Group related layers in the same workspace:
1
2
3
4
5
6
7
| # By dataset
./geoserver-cli workspace create osm_data
./geoserver-cli workspace create census_data
# By environment
./geoserver-cli workspace create prod_layers
./geoserver-cli workspace create test_layers
|
Style Management#
Version Control Your SLD Files#
Keep styles in version control:
1
2
3
4
| styles/
├── roads.sld
├── buildings.sld
└── water.sld
|
Deploy them consistently:
1
2
3
4
5
| for style in styles/*.sld; do
name=$(basename "$style" .sld)
./geoserver-cli style create "$name" --sld "@$style" || \
./geoserver-cli style update "$name" --sld "@$style"
done
|
Use Descriptive Style Names#
Choose clear, meaningful names:
1
2
3
4
5
6
7
| # Good
./geoserver-cli style create road_primary --sld @road_primary.sld
./geoserver-cli style create building_residential --sld @building_residential.sld
# Bad
./geoserver-cli style create style1 --sld @s1.sld
./geoserver-cli style create temp --sld @temp.sld
|
Separate Concerns#
Create focused styles for specific purposes:
1
2
3
4
5
6
| # Base styles
./geoserver-cli style create road_base --sld @road_base.sld
# Themed styles
./geoserver-cli style create road_winter --sld @road_winter.sld
./geoserver-cli style create road_summer --sld @road_summer.sld
|
Security#
Use Read-Only Credentials#
For read-only operations, use limited credentials:
1
2
3
4
| # config for monitoring/reporting
[geoserver]
username = "readonly_user"
password = "readonly_password"
|
Restrict Config File Permissions#
Protect configuration files:
1
2
3
4
5
6
| # Make config readable only by owner
chmod 600 configs/prod.config.toml
# Verify
ls -la configs/prod.config.toml
# Should show: -rw------- (600)
|
Never Log Passwords#
When debugging, redact sensitive information:
1
2
3
4
5
| # Good: Redacted output
./geoserver-cli config show --redact
# Bad: Shows passwords
./geoserver-cli config show
|
Use HTTPS in Production#
Always use HTTPS for production:
1
2
| [geoserver]
base_url = "https://geoserver.example.com/rest" # HTTPS
|
Optimize Bulk Operations#
Use appropriate concurrency:
1
2
3
4
5
6
| # CPU-bound: Use CPU core count
CORES=$(nproc)
./geoserver-cli publish postgis --all -w prod --concurrency $CORES
# Network-bound: Use higher concurrency
./geoserver-cli publish postgis --all -w prod --concurrency 16
|
Adjust Timeouts#
Set appropriate timeouts for your operations:
1
2
3
4
5
| # Quick operations: Short timeout
./geoserver-cli workspace list --timeout 10s
# Bulk publishing: Long timeout
./geoserver-cli publish postgis --all -w prod --timeout 300s
|
Monitor GeoServer#
Keep an eye on GeoServer resources:
1
2
3
4
5
| # Check GeoServer status
curl http://localhost:8080/geoserver/rest/about/status.json
# Watch during bulk operations
watch -n 5 'curl -s http://localhost:8080/geoserver/rest/about/status.json | jq .metrics.memory'
|
Automation and CI/CD#
Use Declarative Configs#
Store desired state in code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # geoserver-config.yaml
workspaces:
- production
- staging
datastores:
production:
- name: postgis
type: postgis
connection: ${POSTGIS_PROD_URL}
layers:
production:
- roads
- buildings
- water
|
Implement Health Checks#
Verify deployments:
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/bin/bash
# health-check.sh
# Check workspace exists
./geoserver-cli workspace get production || exit 1
# Check critical layers
for layer in roads buildings water; do
# Verify layer is enabled and advertised
./geoserver-cli layer update "$layer" | grep -q "Enabled: true" || exit 1
done
echo "Health check passed"
|
Use Rollback Strategies#
Plan for rollback:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #!/bin/bash
# deploy-with-rollback.sh
# Backup current state
BACKUP_DIR="backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Store workspace list
./geoserver-cli workspace list > "$BACKUP_DIR/workspaces.txt"
# Deploy
if ! ./geoserver-cli publish postgis --all -w prod; then
echo "Deployment failed. Rollback instructions:"
echo "1. Check $BACKUP_DIR for previous state"
echo "2. Manually restore via GeoServer UI"
exit 1
fi
|
Log Everything#
Maintain audit logs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
# publish-with-logging.sh
LOG_FILE="logs/publish-$(date +%Y%m%d_%H%M%S).log"
mkdir -p logs
{
echo "=== Publish started at $(date) ==="
echo "User: $(whoami)"
echo "Config: $GEOSRVCLI_CONFIG"
echo ""
./geoserver-cli publish postgis --all -w prod 2>&1
EXIT_CODE=$?
echo ""
echo "=== Publish finished at $(date) with exit code $EXIT_CODE ==="
} | tee "$LOG_FILE"
exit $EXIT_CODE
|
Testing#
Test in Lower Environments First#
Never test in production:
1
2
3
4
5
6
7
8
| # 1. Test in dev
./geoserver-cli publish postgis --all -w dev --config dev
# 2. Test in staging
./geoserver-cli publish postgis --all -w staging --config staging
# 3. Deploy to production
./geoserver-cli publish postgis --all -w prod --config prod
|
Use Dry Run for Testing#
Preview operations without changes:
1
2
3
4
5
| # Test publish without actually publishing
./geoserver-cli publish postgis --all -w prod --dry-run
# Verify configuration
./geoserver-cli config show --config prod --redact
|
Automate Testing#
Include CLI in your test suite:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #!/bin/bash
# test-integration.sh
# Start test GeoServer (docker)
docker-compose up -d geoserver-test
# Wait for startup
sleep 30
# Run integration tests
./geoserver-cli workspace create test --config test
./geoserver-cli publish postgis --layer test_table -w test --config test
# Cleanup
docker-compose down
|
Maintenance#
Regular Configuration Audits#
Periodically review configurations:
1
2
3
4
5
| # Check all configs
for config in configs/*.config.toml; do
echo "Validating $config"
./geoserver-cli config validate --config "$(basename $config .config.toml)"
done
|
Keep CLI Updated#
Update the CLI regularly:
1
2
3
4
5
6
7
8
| # Pull latest
git pull
# Rebuild
go build -o geoserver-cli ./cmd/geoserver-cli
# Verify
./geoserver-cli version
|
Document Your Workflows#
Keep README files for custom workflows:
1
2
3
4
5
6
| # Production Deployment
1. Validate config: `./geoserver-cli config validate --config prod`
2. Dry run: `./geoserver-cli publish postgis --all -w prod --dry-run`
3. Deploy: `./scripts/deploy-prod.sh`
4. Verify: `./scripts/health-check.sh`
|
Summary#
Key Takeaways:
- ✅ Use environment-specific configs
- ✅ Never commit passwords
- ✅ Always dry-run in production
- ✅ Use appropriate concurrency
- ✅ Test in lower environments first
- ✅ Log and audit all operations
- ✅ Keep styles in version control
- ✅ Implement health checks
- ✅ Plan for rollback
- ✅ Document your workflows