Browse Source

update docker

master
Silberengel 2 weeks ago
parent
commit
e472bfd108
  1. 14
      DOCKER.md
  2. 19
      Dockerfile
  3. 32
      docker-compose-prod.yml
  4. 14
      docker-compose.yml
  5. 19
      docker-entrypoint.sh
  6. 30
      internal/config/config.go

14
DOCKER.md

@ -188,7 +188,7 @@ docker buildx build --platform linux/amd64,linux/arm64 -t gitcitadel-online . @@ -188,7 +188,7 @@ docker buildx build --platform linux/amd64,linux/arm64 -t gitcitadel-online .
For production deployment:
1. **Use a reverse proxy** (nginx, Traefik, etc.) in front of the container
1. **Use a reverse proxy** (nginx, Traefik, Apache, etc.) in front of the container
2. **Set up SSL/TLS** certificates
3. **Configure proper logging** and monitoring
4. **Use secrets management** for sensitive configuration
@ -201,6 +201,18 @@ For production deployment: @@ -201,6 +201,18 @@ For production deployment:
memory: 512M
```
## Apache Reverse Proxy Setup
The `docker-compose.yml` is configured to expose the container on port **2323** for Apache reverse proxy integration.
### Port Configuration
The Docker container exposes port 2323 on the host, which maps to port 8080 inside the container. This matches Apache configurations that proxy to `127.0.0.1:2323`.
If you need to use a different port, update `docker-compose.yml`: Change `"2323:8080"` to your desired port mapping.
**Note:** For Plesk-managed Apache servers, configure the reverse proxy settings through the Plesk control panel. The Docker container is ready to accept connections on port 2323.
## Updating
To update to a new version:

19
Dockerfile

@ -54,16 +54,17 @@ RUN if [ ! -f ./static/js/nostr.bundle.js ]; then \ @@ -54,16 +54,17 @@ RUN if [ ! -f ./static/js/nostr.bundle.js ]; then \
# Copy example config (user should mount their own config.yaml)
COPY config.yaml.example ./config.yaml.example
# Create cache directories
RUN mkdir -p cache/media
# Copy entrypoint script
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
# Create non-root user for security
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser && \
chown -R appuser:appuser /app
# node:20-alpine already has a 'node' user with UID 1000
# Change ownership of /app to node user
RUN chown -R node:node /app && \
chmod +x /app/docker-entrypoint.sh
# Switch to non-root user
USER appuser
USER node
# Expose port (default 8080, can be overridden via config)
EXPOSE 8080
@ -72,6 +73,6 @@ EXPOSE 8080 @@ -72,6 +73,6 @@ EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# Run the application
ENTRYPOINT ["/app/gitcitadel-online"]
CMD ["--config", "/app/config.yaml"]
# Run the application via entrypoint script
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["/app/gitcitadel-online", "--config", "/app/config.yaml"]

32
docker-compose-prod.yml

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
version: '3.8'
services:
gitcitadel-online:
image: silberengel/gitcitadel-online:latest
container_name: gitcitadel-online
restart: unless-stopped
ports:
# Expose port 2323 for Apache reverse proxy (maps to container port 8080)
- "2323:8080"
volumes:
# Persist cache directory
# Note: Ensure the host cache directory is writable by UID 1000 (node user)
# Run: sudo chown -R 1000:1000 ./cache (or use 777 permissions)
- ./cache:/app/cache
# Optional: Mount config file to override defaults
# - ./config.yaml:/app/config.yaml:ro
# Optional environment variables (uncomment and set as needed):
# environment:
# - CONFIG_PATH=/app/config.yaml
# - LOG_LEVEL=info
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '1'
memory: 512M

14
docker-compose.yml

@ -8,7 +8,8 @@ services: @@ -8,7 +8,8 @@ services:
container_name: gitcitadel-online
restart: unless-stopped
ports:
- "8080:8080"
# Expose port 2323 for Apache reverse proxy (maps to container port 8080)
- "2323:8080"
volumes:
# Mount config file (create from config.yaml.example)
- ./config.yaml:/app/config.yaml:ro
@ -19,15 +20,14 @@ services: @@ -19,15 +20,14 @@ services:
# - CONFIG_PATH=/app/config.yaml
# Optional: set log level
# - LOG_LEVEL=info
networks:
- gitcitadel-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
gitcitadel-network:
driver: bridge
deploy:
resources:
limits:
cpus: '1'
memory: 512M

19
docker-entrypoint.sh

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
#!/bin/sh
set -e
# Ensure cache/media directory exists and is writable
# This handles both mounted volumes and container-internal directories
if [ ! -d "cache/media" ]; then
mkdir -p cache/media || {
echo "Error: Failed to create cache/media directory. Check permissions." >&2
exit 1
}
fi
# Ensure the cache directory is writable
if [ ! -w "cache" ] || [ ! -w "cache/media" ]; then
echo "Warning: Cache directory may not be writable. Check permissions on mounted volume." >&2
fi
# Execute the main application
exec "$@"

30
internal/config/config.go

@ -39,9 +39,16 @@ type Config struct { @@ -39,9 +39,16 @@ type Config struct {
}
// LoadConfig loads configuration from a YAML file
// If the file doesn't exist, returns a config with all defaults
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
// File doesn't exist, return config with all defaults
config := &Config{}
setDefaults(config)
return config, nil
}
return nil, fmt.Errorf("failed to read config file: %w", err)
}
@ -50,7 +57,18 @@ func LoadConfig(path string) (*Config, error) { @@ -50,7 +57,18 @@ func LoadConfig(path string) (*Config, error) {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
// Set defaults
setDefaults(&config)
return &config, nil
}
// setDefaults applies default values to a config struct
func setDefaults(config *Config) {
if config.WikiIndex == "" {
config.WikiIndex = "naddr1qvzqqqr4tqpzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqyd8wumn8ghj7argv4nx7un9wd6zumn0wd68yvfwvdhk6qgmwaehxw309a6xsetrd96xzer9dshxummnw3erztnrdakszyrhwden5te0dehhxarj9ekxzmnyqyg8wumn8ghj7mn0wd68ytnhd9hx2qghwaehxw309ahx7um5wgh8xmmkvf5hgtngdaehgqg3waehxw309ahx7um5wgerztnrdaksz9thwden5te0v9nkwu3wdehhxarj9ekxzmnyqyv8wumn8ghj7un9d3shjtnwdaehw6r9wfjjucm0d5q3gamnwvaz7tmjv4kxz7fwv3sk6atn9e5k7qgewaehxw309an8yet9d3shjtnndamxy6t59e5x7um5qqhxw6t5vd5hgctyv4kz6urjda4x2cm594jx7cm4d4jkuarpw35k7m3dvfuj6um5v4kxccfdwcknzhekhth"
}
if config.BlogIndex == "" {
config.BlogIndex = "naddr1qvzqqqr4tqpzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqyd8wumn8ghj7argv4nx7un9wd6zumn0wd68yvfwvdhk6qgmwaehxw309a6xsetrd96xzer9dshxummnw3erztnrdakszyrhwden5te0dehhxarj9ekxzmnyqyg8wumn8ghj7mn0wd68ytnhd9hx2qghwaehxw309ahx7um5wgh8xmmkvf5hgtngdaehgqg3waehxw309ahx7um5wgerztnrdaksz9thwden5te0v9nkwu3wdehhxarj9ekxzmnyqyv8wumn8ghj7un9d3shjtnwdaehw6r9wfjjucm0d5q3gamnwvaz7tmjv4kxz7fwv3sk6atn9e5k7qgewaehxw309an8yet9d3shjtnndamxy6t59e5x7um5qqshg6r994nkjarrd96xzer9dskkymr0vukky7fdwd6x2mrvvykhvtf3q3js44"
}
if config.Relays.Feeds == "" {
config.Relays.Feeds = "wss://theforest.nostr1.com"
}
@ -87,13 +105,6 @@ func LoadConfig(path string) (*Config, error) { @@ -87,13 +105,6 @@ func LoadConfig(path string) (*Config, error) {
if config.SEO.DefaultImage == "" {
config.SEO.DefaultImage = "/static/GitCitadel_Graphic_Landscape.png"
}
// Validate required fields
if config.WikiIndex == "" {
return nil, fmt.Errorf("wiki_index is required")
}
return &config, nil
}
// parseRelayList parses a comma-separated relay string into a slice
@ -126,9 +137,6 @@ func (c *Config) GetContactFormRelays() []string { @@ -126,9 +137,6 @@ func (c *Config) GetContactFormRelays() []string {
// Validate validates the configuration
func (c *Config) Validate() error {
if c.WikiIndex == "" {
return fmt.Errorf("wiki_index is required")
}
if c.Relays.Feeds == "" {
return fmt.Errorf("relays.feeds is required")
}

Loading…
Cancel
Save