Long / Large Videos cannot get uploaded back to PeerTube after transcoded on Remote Runner (Apache)

Describe the current behavior

Im using PeerTube in a docker Container on a Raspberry Pi 4.
I’m using Apache Reverse Proxy to make it available through the subdomain https://peertube.mydomain.de/
I’m trying to import Videos from YouTube (3 hour, 1080p). I already tried to increase the Upload Limit in Apache (see Config below). The Import of smaller YouTube Videos (2.5 hours, 480p) worked just fine, even without the Increase-Settings in Apache.

What happens after importing Video?

→ Import works
→ Video gets send to Remote Runner
→ Gets transcoded on my Windows-Machine (better performance than the Raspberry Pi)
→ Windows machine Uploads Video back to Raspberry Pi
→ Fails to upload (Expected status 204, got 502). Here the logs from the docker:

[peertube.mydomain.de:443] 2025-10-12 13:17:03.484 error: Remote runner RemoteRunnerName had an error with job 943ba415-6127-469c-bd4a-6384c0090832 (vod-web-video-transcoding) {
  "errorMessage": "Expected status 204, got 502. \nThe server responded: \"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>502 Bad Gateway</title>\n</head><body>\n<h1>Bad Gateway</h1>\n<p>The proxy server received an invalid\r\nresponse from an upstream server.<br />\r\n</p>\n<hr>\n<address>Apache/2.4.65 (Debian) Server at peertube.mydomain.de Port 443</address>\n</body></html>\n\".\nYou may take a closer look at the logs. To see how to do so, check out this page: https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/development/tests.md#debug-server-logs",
  "totalFailures": 4
}
[peertube.mydomain.de:443] 2025-10-12 13:17:03.514 info: <Public IP-Address redacted> - - [12/Oct/2025:13:17:03 +0000] "POST /api/v1/runners/jobs/request HTTP/1.1" 200 20 "-" "-"
[peertube.mydomain.de:443] 2025-10-12 13:17:03.551 info: <Public IP-Address redacted> - - [12/Oct/2025:13:17:03 +0000] "POST /api/v1/runners/jobs/943ba415-6127-469c-bd4a-6384c0090832/error HTTP/1.1" 204 - "-" "-"
[peertube.mydomain.de:443] 2025-10-12 13:17:04.738 info: <Public IP-Address redacted> - - [12/Oct/2025:13:17:04 +0000] "POST /api/v1/runners/jobs/request HTTP/1.1" 200 353 "-" "-"
[peertube.mydomain.de:443] 2025-10-12 13:17:04.879 info: Remote runner RemoteRunnerName has accepted job 943ba415-6127-469c-bd4a-6384c0090832 (vod-web-video-transcoding)
[peertube.mydomain.de:443] 2025-10-12 13:17:04.889 info: <Public IP-Address redacted> - - [12/Oct/2025:13:17:04 +0000] "POST /api/v1/runners/jobs/943ba415-6127-469c-bd4a-6384c0090832/accept HTTP/1.1" 200 673 "-" "-"
[peertube.mydomain.de:443] 2025-10-12 13:17:05.095 info: <Public IP-Address redacted> - - [12/Oct/2025:13:17:05 +0000] "POST /api/v1/runners/jobs/943ba415-6127-469c-bd4a-6384c0090832/update HTTP/1.1" 204 - "-" "-"
[peertube.mydomain.de:443] 2025-10-12 13:17:05.381 info: Get max quality file of video 1d8f4faf-0218-4f8e-9830-44a11533144c of job 943ba415-6127-469c-bd4a-6384c0090832 for runner RemoteRunnerName

→ Here the Logs from Remote Runner

err: {
  "type": "Error",
  "message": "Expected status 204, got 502. \nThe server responded: \"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>502 Bad Gateway</title>\n</head><body>\n<h1>Bad Gateway</h1>\n<p>The proxy server received an invalid\r\nresponse from an upstream server.<br />\r\n</p>\n<hr>\n<address>Apache/2.4.65 (Debian) Server at peertube.mydomain.de Port 443</address>\n</body></html>\n\".\nYou may take a closer look at the logs. To see how to do so, check out this page: https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/development/tests.md#debug-server-logs",
  "stack":
	  Error: Expected status 204, got 502. 
	  The server responded: "<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
	  <html><head>
	  <title>502 Bad Gateway</title>
	  </head><body>
	  <h1>Bad Gateway</h1>
	  <p>The proxy server received an invalid
	  response from an upstream server.<br />
	  </p>
	  <hr>
	  <address>Apache/2.4.65 (Debian) Server at peertube.mydomain.de Port 443</address>
	  </body></html>
	  ".
	  You may take a closer look at the logs. To see how to do so, check out this page: https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/development/tests.md#debug-server-logs
		  at buildRequest (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:71618:14)
		  at makeUploadRequest (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:71535:9)
		  at RunnerJobsCommand.postUploadRequest (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:71702:12)
		  at RunnerJobsCommand.uploadRunnerJobRequest (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:72544:16)
		  at RunnerJobsCommand.success (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:72522:17)
		  at processWebVideoTranscoding (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:81869:29)
		  at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
		  at async processJob (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:90926:7)
	  ----
		  at file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:71621:19
		  at file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:42376:17
		  at Test._assertFunction (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:42363:17)
		  at Test.assert (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:42232:27)
		  at localAssert (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:42191:18)
		  at file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:42204:11
		  at Request3.callback (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:41740:7)
		  at IncomingMessage.<anonymous> (file:///C:/Users/<redacted>/AppData/Roaming/npm/node_modules/@peertube/peertube-runner/dist/peertube-runner.js:41920:22)
		  at IncomingMessage.emit (node:events:525:35)
		  at endReadableNT (node:internal/streams/readable:1696:12)
  "res": {
	"req": {
	  "method": "POST",
	  "url": "https://peertube.mydomain.de/api/v1/runners/jobs/943ba415-6127-469c-bd4a-6384c0090832/success",
	  "headers": {
		"accept": "application/json"
	  }
	},
	"header": {
	  "date": "Sun, 12 Oct 2025 13:10:46 GMT",
	  "server": "Apache/2.4.65 (Debian)",
	  "content-length": "318",
	  "connection": "close",
	  "content-type": "text/html; charset=iso-8859-1"
	},
	"status": 502,
	"text": "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>502 Bad Gateway</title>\n</head><body>\n<h1>Bad Gateway</h1>\n<p>The proxy server received an invalid\r\nresponse from an upstream server.<br />\r\n</p>\n<hr>\n<address>Apache/2.4.65 (Debian) Server at peertube.mydomain.de Port 443</address>\n</body></html>\n"
  }
}

/opt/peertube/docker-compose:

services:
  peertube:
    image: chocobozzz/peertube:production-bookworm
    networks:
      default:
        ipv4_address: 172.20.0.42
        ipv6_address: fdab:e4b3:21a2:ef1b::42
    env_file:
      - .env

    ports:
     - "1935:1935"
     - "9000:9000"
    volumes:
      - /mnt/5TB/peertube/data:/data
      - /mnt/5TB/peertube/config:/config
    depends_on:
      - postgres
      - redis
    restart: "always"

  postgres:
    image: postgres:17-alpine
    env_file:
      - .env
    volumes:
      - ./docker-volume/db:/var/lib/postgresql/data
    restart: "always"

  redis:
    image: redis:8-alpine
    volumes:
      - ./docker-volume/redis:/data
    restart: "always"

networks:
  default:
    enable_ipv6: true
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
      - subnet: fdab:e4b3:21a2:ef1b::/64

volumes:
  assets:
  certbot-www:

/opt/peertube/.env:

# Database / Postgres service configuration
POSTGRES_USER=peertube
POSTGRES_PASSWORD=<redacted>
# Postgres database name "peertube"
POSTGRES_DB=peertube
# The database name used by PeerTube will be PEERTUBE_DB_NAME (only if set) *OR* 'peertube'+PEERTUBE_DB_SUFFIX
#PEERTUBE_DB_NAME=<MY POSTGRES DB NAME>
#PEERTUBE_DB_SUFFIX=_prod
# Database username and password used by PeerTube must match Postgres', so they are copied:
PEERTUBE_DB_USERNAME=$POSTGRES_USER
PEERTUBE_DB_PASSWORD=$POSTGRES_PASSWORD
PEERTUBE_DB_SSL=false
# Default to Postgres service name "postgres" in docker-compose.yml
PEERTUBE_DB_HOSTNAME=postgres

# PeerTube server configuration
# If you test PeerTube in local: use "peertube.localhost" and add this domain to your host file resolving on 127.0.0.1
PEERTUBE_WEBSERVER_HOSTNAME=peertube.mydomain.de
# If you just want to test PeerTube on local
PEERTUBE_WEBSERVER_PORT=443
PEERTUBE_WEBSERVER_HTTPS=true
# If you need more than one IP as trust_proxy
# pass them as a comma separated array:
PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "::1", "172.20.0.0/16"]

# Generate one using `openssl rand -hex 32`
PEERTUBE_SECRET=<redacted>

# E-mail configuration
# If you use a Custom SMTP server
PEERTUBE_SMTP_USERNAME=<redacted>
PEERTUBE_SMTP_PASSWORD=<redacted>
# Default to Postfix service name "postfix" in docker-compose.yml
# May be the hostname of your Custom SMTP server
PEERTUBE_SMTP_HOSTNAME=<redacted>
PEERTUBE_SMTP_PORT=<redacted>
PEERTUBE_SMTP_FROM=<redacted>
PEERTUBE_SMTP_TLS=true
PEERTUBE_SMTP_DISABLE_STARTTLS=true
PEERTUBE_ADMIN_EMAIL=<redacted>

# Postfix service configuration
POSTFIX_myhostname=peertube.mydomain.de
# If you need to generate a list of sub/DOMAIN keys
# pass them as a whitespace separated string <DOMAIN>=<selector>
OPENDKIM_DOMAINS=peertube.mydomain.de=peertube
# see https://github.com/wader/postfix-relay/pull/18
OPENDKIM_RequireSafeKeys=no

# If you want to enable object storage for PeerTube, set the following variables.
#PEERTUBE_OBJECT_STORAGE_ENABLED=
#PEERTUBE_OBJECT_STORAGE_ENDPOINT=
#PEERTUBE_OBJECT_STORAGE_REGION=
#PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID=
#PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY=
#PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME=
#PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX=
#PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BASE_URL=
#PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME=
#PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX=
#PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BASE_URL=
#PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BUCKET_NAME=
#PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_PREFIX=
#PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BASE_URL=
#PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BUCKET_NAME=
#PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_PREFIX=
#PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BASE_URL=
#PEERTUBE_OBJECT_STORAGE_CAPTIONS_BUCKET_NAME=
#PEERTUBE_OBJECT_STORAGE_CAPTIONS_PREFIX=
#PEERTUBE_OBJECT_STORAGE_CAPTIONS_BASE_URL=

# Comment these variables if your S3 provider does not support object ACL
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC="public-read"
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE="private"

#PEERTUBE_LOG_LEVEL=info

# /!\ Prefer to use the PeerTube admin interface to set the following configurations /!\
#PEERTUBE_SIGNUP_ENABLED=true
#PEERTUBE_TRANSCODING_ENABLED=true
#PEERTUBE_CONTACT_FORM_ENABLED=true

/etc/apache2/sites-available/peertube.mydomain.de-se-ssl.conf:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName peertube.mydomain.de

    SSLEngine On
    SSLCertificateFile /etc/letsencrypt/live/mydomain.de/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.de/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    # ----------------------------------------------------
    # PeerTube Reverse Proxy – "unlimited" Upload-Modus
    # ----------------------------------------------------
    ProxyRequests Off
    ProxyPreserveHost On
    AllowEncodedSlashes NoDecode
    LimitRequestBody 0

    # 24 Stunden Timeout (Apache akzeptiert max int)
    Timeout 86400
    ProxyTimeout 86400
    ProxyBadHeader Ignore
    SetEnv proxy-sendchunked 1
    RequestReadTimeout header=0 body=0

    # Erlaube riesige Uploads, kein KeepAlive-Limit
    KeepAlive On
    MaxKeepAliveRequests 0
    KeepAliveTimeout 86400

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    # ----------------------------------------------------
    # WebSocket Weiterleitung
    # ----------------------------------------------------
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:9000/$1 [P,L]

    # ----------------------------------------------------
    # Proxy zu PeerTube im Docker
    # ----------------------------------------------------
    ProxyPass / http://127.0.0.1:9000/ nocanon disablereuse=on retry=0 connectiontimeout=600 timeout=86400
    ProxyPassReverse / http://127.0.0.1:9000/

    ErrorLog ${APACHE_LOG_DIR}/peertube_error.log
    CustomLog ${APACHE_LOG_DIR}/peertube_access.log combined
</VirtualHost>
</IfModule>

Steps to reproduce

  1. Use Apache ReverseProxy and Remote Runner with my settings
  2. Import Long YouTube-Video
  3. See Remote Runner teasing you

Describe the expected behavior

Upload of transcoded Video should be working.

Additional information

  • PeerTube instance:

    • URL: https://peertube.mydomain.de
    • Version: 7.3.0 (Docker)
    • NodeJS version (Runner): v23.3.0
    • Ffmpeg version (Runner): 7.1-full_build-www.gyan.dev
  • Browser name, version and platforms on which you could reproduce the bug: Debian (Raspberry Pi, PeerTube Instance) / Windows 11 (PeerTube Runner)

Hi renthner

Is there anything interesting to see at Windows 11 runner side ?
Here i can’t erproduce since i don’t have such setup home, no docker on my Raspberry Pi 4, no external runner, and another OS.

Regards

This is everything I have from Windows. My Remote Runner Config.toml is here:

[jobs]
concurrency = 1

[ffmpeg]
threads = 32
nice = 20

[transcription]
engine = "whisper-ctranslate2"
model = "large-v2"

[[registeredInstances]]
url = "https://peertube.mydomain.de"
runnerToken = "<redacted>"
runnerName = "Remote_Runner"

[server.ipc]
enabled = false

@renthner

I restart reading the whole problem, in description you stated

‘The Import of smaller YouTube Videos (2.5 hours, 480p) worked just fine, even without the Increase-Settings in Apache.’

From the Remote Runner we see that task completed with succes but while POSTing result to peertube back it went with a 502 error, meaning apache got a problem during its passing information to peertube node process.

This looks like peertube node process has problems to handle a huge result, then i would more focus on peertube side. Perhaps here we hit a limitation on raspberry pi4.

I got to the problem.
I event tried switching from Apache to NGINX, but it wasn’t the solution. Now I installed PeerTube directly on the Raspberry Pi and was able to edit the production.yaml (Copied from default.yaml) at the important part. I changed it from « 5 minutes » to « 20 minutes » and now everything works fine.
But it would be awesome, if this Option would be easily accesable in a Docker Container.
Maybe it is, but I wasn’t able to find it.

1 Like

@renthner

Good you find a way ! This 502 err is not that good, and fact that video transcoding is sent all in one POST is an issue too. An improvement was requested Runners should be capable of chunked upload. · Issue #5888 · Chocobozzz/PeerTube · GitHub that might improve this.

Still you can change configuration of your peertube docker installation since docker-volume of peertube is persistent.

production.yaml of your peertube is in your docker-volume /mnt/5TB/peertube/config:/config

stop docker with docker compose down, edit production.yaml within config directory and restart with docker compose up. Should do it.

Ah, I didn’t have a production.yaml in my /mnt/5TB/peertube/config:/config. Would it be possible to just create it from the default.yaml?

Keep both, default.yaml is a safe net and contains all the default for you current version. It is not a good idea to edit it.

Copying it to production.yaml will work and content of production should take precedence, configuration are read in order, first default then production.yaml that overrides content, then maybe local ones …

but production does not requires to set everything, it is better it set only what is not defaut.

Awesome, thank you. I hope this also helps some people in the future.