A lightweight, fast, and feature-rich static file server written in Go. Perfect for development, testing, and production.
- β HTTP/HTTPS static file server
- β Single executable with no dependencies
- β Cross-platform (Linux, Windows, macOS)
- β Configuration via JSON file or command-line flags
- β Hot reload configuration
- π HTTPS/TLS support
- π Basic authentication (username/password)
- π Configurable CORS
- π Rate limiting per IP
- π IP whitelist/blacklist
- π Path traversal protection
- π Hidden file blocking (.env, .git, etc.)
- π Automatic security headers
- β‘ Gzip compression with configurable levels
- β‘ ETags for efficient caching
- β‘ Configurable cache headers
- β‘ Custom HTTP headers
- β‘ Configurable timeouts
- π Optional directory listing
- π Custom index files
- π― SPA (Single Page Application) mode
- π¨ Custom error pages
- π Detailed colored logs
- π Separate access and error logs
- π§ Runtime config for containers/Kubernetes
Download the latest release for your platform from the releases page.
# Download and extract
tar -xzf koryx-serv-linux-amd64.tar.gz
# Make executable and move to PATH
chmod +x koryx-serv-linux-amd64
sudo mv koryx-serv-linux-amd64 /usr/local/bin/koryx-serv
# Verify installation
koryx-serv -version# Download and extract koryx-serv-windows-amd64.zip
# Add to PATH or run directly
.\koryx-serv-windows-amd64.exe -versiongit clone https://github.com/koryxio/koryx-serv.git
cd koryx-serv
go build -o koryx-serv ./cmd/koryx-serv# Build for current platform
make build
# Build for all platforms
make build-all
# Create release archives
make release-local
# See all options
make help# Serve current directory on port 8080
./koryx-serv
# Serve a specific directory
./koryx-serv -dir /var/www
# Custom port
./koryx-serv -port 3000
# Enable directory listing
./koryx-serv -list# Generate example configuration
./koryx-serv -generate-config config.json
# Start with configuration
./koryx-serv -config config.jsonThe configuration file uses JSON format. Complete example:
{
"server": {
"port": 8080,
"host": "0.0.0.0",
"root_dir": ".",
"read_timeout": 30,
"write_timeout": 30
},
"security": {
"enable_https": false,
"cert_file": "/path/to/cert.pem",
"key_file": "/path/to/key.pem",
"basic_auth": {
"enabled": true,
"username": "admin",
"password": "secret",
"realm": "Restricted Area"
},
"cors": {
"enabled": true,
"allowed_origins": ["https://example.com"],
"allowed_methods": ["GET", "POST", "OPTIONS"],
"allowed_headers": ["*"],
"allow_credentials": true,
"max_age": 3600
},
"rate_limit": {
"enabled": true,
"requests_per_ip": 100,
"burst_size": 20
},
"ip_whitelist": ["192.168.1.100", "10.0.0.50"],
"ip_blacklist": ["192.168.1.200"],
"block_hidden_files": true
},
"performance": {
"enable_compression": true,
"compression_level": 6,
"enable_cache": true,
"cache_max_age": 3600,
"enable_etags": true,
"custom_headers": {
"X-Powered-By": "koryx-serv"
}
},
"logging": {
"enabled": true,
"level": "info",
"access_log": true,
"error_log": true,
"log_file": "",
"color_output": true
},
"features": {
"directory_listing": false,
"index_files": ["index.html", "index.htm"],
"spa_mode": false,
"spa_index": "index.html",
"custom_error_pages": {
"404": "404.html",
"403": "403.html"
}
},
"runtime_config": {
"enabled": false,
"route": "/runtime-config.js",
"format": "js",
"var_name": "APP_CONFIG",
"env_prefix": "APP_",
"env_variables": [],
"no_cache": true
}
} -config string
Path to configuration file (JSON). Overrides KORYX_CONFIG and /app/config.json auto-discovery.
-port int
Port to listen on (overrides config)
-host string
Host to bind to (overrides config)
-dir string
Root directory to serve (overrides config)
-list
Enable directory listing
-generate-config string
Generate example config file and exit
-version
Show version and exit
-help
Show this help message
Configuration precedence:
-configflagKORYX_CONFIGenvironment variable/app/config.json(if present)- Built-in defaults
koryx-serv can also be imported by another Go service.
import (
"net/http"
koryxserv "koryx-serv"
)
func mountStatic(mux *http.ServeMux) error {
cfg := koryxserv.DefaultConfig()
cfg.Server.RootDir = "./public"
logger, err := koryxserv.NewLogger(&cfg.Logging)
if err != nil {
return err
}
staticHandler, err := koryxserv.NewHandler(cfg, logger)
if err != nil {
return err
}
mux.Handle("/static/", http.StripPrefix("/static", staticHandler))
return nil
}Notes:
- The CLI entrypoint lives in
./cmd/koryx-serv. - Core reusable package lives at module root (
package koryxserv).
# Serve React/Vue/Angular app
./koryx-serv -dir ./dist -port 3000 -listCreate a config.json:
{
"server": {
"port": 8080,
"root_dir": "./dist"
},
"features": {
"spa_mode": true,
"spa_index": "index.html"
}
}./koryx-serv -config config.json{
"server": {
"port": 8080,
"root_dir": "./files"
},
"security": {
"basic_auth": {
"enabled": true,
"username": "admin",
"password": "mypassword",
"realm": "Private Files"
},
"block_hidden_files": true
}
}# Generate self-signed certificate for testing
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes{
"server": {
"port": 8443,
"root_dir": "."
},
"security": {
"enable_https": true,
"cert_file": "cert.pem",
"key_file": "key.pem"
}
}{
"server": {
"port": 8080,
"root_dir": "./api"
},
"security": {
"cors": {
"enabled": true,
"allowed_origins": ["http://localhost:3000", "https://myapp.com"],
"allowed_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allowed_headers": ["Content-Type", "Authorization"],
"allow_credentials": true
}
}
}{
"server": {
"port": 80,
"root_dir": "/var/www/html"
},
"security": {
"rate_limit": {
"enabled": true,
"requests_per_ip": 100,
"burst_size": 20
},
"block_hidden_files": true
},
"performance": {
"enable_compression": true,
"compression_level": 9,
"enable_cache": true,
"cache_max_age": 86400,
"enable_etags": true
},
"logging": {
"enabled": true,
"level": "info",
"access_log": true,
"error_log": true,
"log_file": "/var/log/koryx-serv.log"
}
}Serve dynamic configuration from environment variables - perfect for containerized applications.
Use Case: Deploy the same Docker image to dev/staging/prod with different configurations.
{
"server": {
"port": 8080,
"root_dir": "/app/build"
},
"features": {
"spa_mode": true
},
"runtime_config": {
"enabled": true,
"route": "/runtime-config.js",
"format": "js",
"var_name": "APP_CONFIG",
"env_prefix": "APP_",
"no_cache": true
}
}Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: frontend
image: myapp:latest
env:
- name: APP_API_URL
value: "https://api.production.com"
- name: APP_VERSION
value: "v1.2.3"Frontend Usage:
<!-- Load runtime config -->
<script src="/runtime-config.js"></script>
<script>
// Access config
fetch(window.APP_CONFIG.API_URL + "/users");
</script>Output (/runtime-config.js):
window.APP_CONFIG = {
API_URL: "https://api.production.com",
VERSION: "v1.2.3",
};π See RUNTIME_CONFIG.md for complete documentation with Docker/Kubernetes examples, security best practices, and integration guides for React/Vue/Angular.
-
Always block hidden files in production:
"block_hidden_files": true
-
Use HTTPS in production:
"enable_https": true
-
Implement rate limiting to prevent DDoS attacks:
"rate_limit": { "enabled": true, "requests_per_ip": 100 }
-
Use authentication for sensitive content:
"basic_auth": { "enabled": true, "username": "admin", "password": "strong-password" }
-
Whitelist IPs when possible:
"ip_whitelist": ["192.168.1.0/24"]
- Compression: Enable gzip to reduce response sizes
- Cache: Configure
cache_max_ageappropriately - ETags: Reduces unnecessary transfers
- Timeouts: Configure to avoid hanging connections
# Install benchmarking tool
go install github.com/rakyll/hey@latest
# Test performance
hey -n 10000 -c 100 http://localhost:8080/βββββββββββββββββββββββββββββββββββββββββ
β β
β KORYX SERV - File Server β
β β
βββββββββββββββββββββββββββββββββββββββββ
[2025-10-28 14:30:00] [INFO] Server starting...
[2025-10-28 14:30:00] [INFO] Protocol: HTTP
[2025-10-28 14:30:00] [INFO] Host: 0.0.0.0
[2025-10-28 14:30:00] [INFO] Port: 8080
[2025-10-28 14:30:00] [INFO] Root Directory: .
[2025-10-28 14:30:00] [INFO] Compression: Enabled (level 6)
[2025-10-28 14:30:00] [INFO] β Server running at http://0.0.0.0:8080
[2025-10-28 14:30:00] [INFO] Press Ctrl+C to stop
[2025-10-28 14:30:15] GET /index.html - 200 - 15.2ms - 192.168.1.100
[2025-10-28 14:30:16] GET /style.css - 200 - 8.5ms - 192.168.1.100
[2025-10-28 14:30:17] GET /app.js - 200 - 12.1ms - 192.168.1.100
# Pull latest image
docker pull koryxio/koryx-serv:latest
# Run and serve local ./public on port 8080
docker run --rm \
-p 8080:8080 \
-v "$(pwd)/public:/app/public:ro" \
koryxio/koryx-serv:latestWith custom config:
docker run --rm \
-p 8080:8080 \
-v "$(pwd)/public:/app/public:ro" \
-v "$(pwd)/config.json:/app/config.json:ro" \
koryxio/koryx-serv:latestservices:
koryx-serv:
image: koryxio/koryx-serv:latest
container_name: koryx-serv
ports:
- "8080:8080"
volumes:
- ./public:/app/public:ro
# Optional custom config:
# - ./config.json:/app/config.json:ro
restart: unless-stoppedCreate a Dockerfile:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -ldflags="-s -w" -o koryx-serv ./cmd/koryx-serv
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/koryx-serv .
EXPOSE 8080
CMD ["./koryx-serv"]Build and run:
docker build -t serve .
docker run -p 8080:8080 -v $(pwd):/root/files serve -dir /root/filesContributions are welcome! Please:
- Fork the project
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
See CONTEXT.md for detailed development documentation, architecture decisions, and project structure.
MIT License - see the LICENSE file for details.
- π Bug Reports
- π‘ Feature Requests
- π Documentation
See CHANGELOG.md for a list of changes.
Made with β€οΈ in Go