Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ services:

pinepods:
image: madeofpendletonwool/pinepods:latest
user: "1000:1000"
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
ports:
- "8040:8040"
environment:
Expand All @@ -38,18 +43,13 @@ services:
VALKEY_PORT: 6379
# Enable or Disable Debug Mode for additional Printing
DEBUG_MODE: false
PUID: ${UID:-911}
PGID: ${GID:-911}
# Add timezone configuration
TZ: "America/New_York"

volumes:
# Mount the download and backup locations on the server
# Host directories must be owned by UID 1000 before starting.
# Run: sudo chown -R 1000:1000 /home/user/pinepods/downloads /home/user/pinepods/backups
- /home/user/pinepods/downloads:/opt/pinepods/downloads
- /home/user/pinepods/backups:/opt/pinepods/backups
# Timezone volumes, HIGHLY optional. Read the timezone notes below
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
depends_on:
- db
- valkey
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ services:

pinepods:
image: madeofpendletonwool/pinepods:latest
user: "1000:1000"
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
ports:
- "8040:8040"
environment:
Expand All @@ -38,23 +43,15 @@ services:
VALKEY_PORT: 6379
# Enable or Disable Debug Mode for additional Printing
DEBUG_MODE: false
PUID: ${UID:-911}
PGID: ${GID:-911}
# Add timezone configuration
TZ: "America/New_York"
# Language Configuration
DEFAULT_LANGUAGE: "en"
volumes:
# Mount the download and the backup location on the server if you want to. You could mount a nas to the downloads folder or something like that.
# The backups directory is used if backups are made on the web version on pinepods. When taking backups on the client version it downloads them locally.

volumes:
# Mount the download and backup locations on the server
# Host directories must be owned by UID 1000 before starting.
# Run: sudo chown -R 1000:1000 /home/user/pinepods/downloads /home/user/pinepods/backups
- /home/user/pinepods/downloads:/opt/pinepods/downloads
- /home/user/pinepods/backups:/opt/pinepods/backups
# Timezone volumes, HIGHLY optional. Read the timezone notes below
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
depends_on:
- db
- valkey
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ services:

pinepods:
image: madeofpendletonwool/pinepods:latest
user: "1000:1000"
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
ports:
- "8040:8040"
environment:
Expand All @@ -36,19 +41,15 @@ services:
VALKEY_PORT: 6379
# Enable or Disable Debug Mode for additional Printing
DEBUG_MODE: false
PUID: ${UID:-911}
PGID: ${GID:-911}
# Add timezone configuration
TZ: "America/New_York"
# Language Configuration
DEFAULT_LANGUAGE: "en"
volumes:
# Mount the download and backup locations on the server
# Host directories must be owned by UID 1000 before starting.
# Run: sudo chown -R 1000:1000 /home/user/pinepods/downloads /home/user/pinepods/backups
- /home/user/pinepods/downloads:/opt/pinepods/downloads
- /home/user/pinepods/backups:/opt/pinepods/backups
# Timezone volumes, HIGHLY optional. Read the timezone notes below
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
restart: always
depends_on:
- db
Expand Down
40 changes: 39 additions & 1 deletion dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ LABEL maintainer="Collin Pendleton <collinp@collinpendleton.com>"
# Install runtime dependencies
RUN apk add --no-cache tzdata nginx openssl bash mariadb-client postgresql-client curl ffmpeg wget jq mariadb-connector-c-dev

# Create a non-root user and group with fixed UID/GID 1000
RUN addgroup -g 1000 pinepods && \
adduser -u 1000 -G pinepods -S -D -h /home/pinepods pinepods


# Download and install latest yt-dlp binary (musllinux for Alpine)
RUN LATEST_VERSION=$(curl -s https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest | jq -r .tag_name) && \
Expand Down Expand Up @@ -132,6 +136,8 @@ RUN chmod +x /startup.sh
RUN mkdir -p /pinepods
RUN mkdir -p /var/log/pinepods/ && mkdir -p /etc/horust/services/
COPY startup/ /pinepods/startup/
# Pre-populate Horust services directory at build time (avoids runtime root write)
COPY startup/services/*.toml /etc/horust/services/
# Legacy cron scripts removed - background tasks now handled by internal Rust scheduler
COPY clients/ /pinepods/clients/
COPY database_functions/ /pinepods/database_functions/
Expand All @@ -151,8 +157,40 @@ RUN chmod +x /usr/local/bin/start-gpodder.sh
RUN cp /usr/share/zoneinfo/UTC /etc/localtime && \
echo "UTC" > /etc/timezone

# Pre-create all runtime directories and set ownership to pinepods (1000:1000)
# so the container can run without root privileges
RUN mkdir -p \
/opt/pinepods/downloads \
/opt/pinepods/backups \
/opt/pinepods/certs \
/pinepods/cache \
/var/log/pinepods \
/etc/horust/services \
/var/lib/nginx \
/var/log/nginx \
/run/nginx \
/tmp/nginx && \
chown -R pinepods:pinepods \
/opt/pinepods \
/pinepods \
/var/log/pinepods \
/var/www/html \
/etc/horust \
/var/lib/nginx \
/var/log/nginx \
/run/nginx \
/tmp/nginx \
/startup.sh \
/usr/local/bin/pinepods-api \
/usr/local/bin/gpodder-api \
/usr/local/bin/pinepods-db-setup \
/usr/local/bin/start-gpodder.sh

# Switch to non-root user
USER pinepods:pinepods

# Expose ports
EXPOSE 8080 8000
EXPOSE 8040

# Start everything using the startup script
ENTRYPOINT ["bash", "/startup.sh"]
10 changes: 10 additions & 0 deletions startup/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Run without root - write PID to a user-writable location
pid /tmp/nginx.pid;

events {}

http {
# Redirect nginx temp/cache paths to writable locations for non-root operation
client_body_temp_path /tmp/nginx/client_body;
proxy_temp_path /tmp/nginx/proxy;
fastcgi_temp_path /tmp/nginx/fastcgi;
uwsgi_temp_path /tmp/nginx/uwsgi;
scgi_temp_path /tmp/nginx/scgi;

include mime.types;
default_type application/octet-stream;
client_max_body_size 0;
Expand Down
65 changes: 13 additions & 52 deletions startup/startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,39 +86,17 @@ A project created and written by Collin Pendleton
collinp@gooseberrydevelopment.com
EOF

# Configure timezone based on TZ environment variable
# Timezone is configured via the TZ environment variable, which musl/glibc
# respect without needing to write to /etc/localtime (a root-owned file).
# The TZ variable is already exported above and will be inherited by all
# child processes started by Horust.
if [ -n "$TZ" ]; then
echo "Setting timezone to $TZ"
# For Alpine, we need to copy the zoneinfo file
if [ -f "/usr/share/zoneinfo/$TZ" ]; then
# Check if /etc/localtime is a mounted volume
if [ -f "/etc/localtime" ] && ! [ -L "/etc/localtime" ]; then
echo "Using mounted timezone file from host"
else
# If it's not mounted or is a symlink, we can modify it
cp /usr/share/zoneinfo/$TZ /etc/localtime
echo "$TZ" > /etc/timezone
fi
else
echo "Timezone $TZ not found, using UTC"
# Only modify if not mounted
if ! [ -f "/etc/localtime" ] || [ -L "/etc/localtime" ]; then
cp /usr/share/zoneinfo/UTC /etc/localtime
echo "UTC" > /etc/timezone
fi
fi
echo "Timezone set to $TZ via TZ environment variable"
else
echo "No timezone specified, using UTC"
# Only modify if not mounted
if ! [ -f "/etc/localtime" ] || [ -L "/etc/localtime" ]; then
cp /usr/share/zoneinfo/UTC /etc/localtime
echo "UTC" > /etc/timezone
fi
echo "No timezone specified, defaulting to UTC"
export TZ=UTC
fi

# Export TZ to the environment for all child processes
export TZ

# Create required directories
echo "Creating required directories..."
mkdir -p /pinepods/cache
Expand All @@ -136,17 +114,8 @@ echo "Database validation complete"

# Cron jobs removed - now handled by internal Rust scheduler

# Check if we need to create exim directories
# Only do this if the user/group exists on the system
if getent group | grep -q "Debian-exim"; then
echo "Setting up exim directories and permissions..."
mkdir -p /var/log/exim4
mkdir -p /var/spool/exim4
chown -R Debian-exim:Debian-exim /var/log/exim4
chown -R Debian-exim:Debian-exim /var/spool/exim4
else
echo "Skipping exim setup as user/group doesn't exist on this system"
fi
# Exim setup skipped - container runs as non-root (pinepods, UID 1000) and
# cannot chown system directories.

# Set up environment variables for Horust logging modes
if [[ $DEBUG_MODE == "true" ]]; then
Expand All @@ -159,18 +128,10 @@ else
echo "Starting Horust in production mode (logs to files)..."
fi

# Set permissions for download and backup directories BEFORE starting services
# Only do this if PUID and PGID are set
if [[ -n "$PUID" && -n "$PGID" ]]; then
echo "Setting permissions for download and backup directories...(Be patient this might take a while if you have a lot of downloads)"
chown -R ${PUID}:${PGID} /opt/pinepods/downloads
chown -R ${PUID}:${PGID} /opt/pinepods/backups
else
echo "Skipping permission setting as PUID/PGID are not set"
fi

# Copy service configurations to Horust directory
cp /pinepods/startup/services/*.toml /etc/horust/services/
# Volume permissions are no longer set at runtime. The container runs as
# pinepods (UID 1000, GID 1000). Host directories mounted at
# /opt/pinepods/downloads and /opt/pinepods/backups must be owned by
# UID 1000 on the host before starting the container.

# Start all services with Horust
echo "Starting services with Horust..."
Expand Down