Passing params to docker run

I have a container that I run with the following command:

"docker run -p 3000:3000 -i --init --rm --cap-add=SYS_ADMIN --name codestrap-website node -e \"`cat server.js`\""

How can I pass the --cap-add=SYS_ADMIN and node -e \"cat server.js\" params to the docker run command in fly?

Could an entry script work?

I would allow me to start the node server. But --cap-add=SYS_ADMIN adds linux admin permissions required in my application (puppeteer). MAybe there is a way to add these permissions in the Dockerfile?

You should have full root access to your VMs by default. The arguments came to tweak Docker don’t apply to your app on Fly. Are you getting permission errors?

I get an Unable to open X display error without adding SYS_ADMIN. When google-chrome-stable is run it barfs about some permissions issue that ends up leading to this inability to locate it display. Under the hood I think puppeteer is trying to use xvfb.

I get an Unable to open X display error without adding SYS_ADMIN. When google-chrome-stable is run it barfs about some permissions issue that ends up leading to this inability to locate it display. Under the hood I think puppeteer is trying to use xvfb.

I think what is going on here is my setup starts puppeteer with Chrome using CMD ["google-chrome-stable"]. PUPPETEER_SKIP_CHROMIUM_DOWNLOAD is false. This enables full media support (at least that’s what I assume is happening). However your example app uses:

const browser = await puppeteer.launch(
      {
        executablePath: process.env.CHROME_BIN,

Does fly.io provide a Chrome bin? If so I can modify my setup for fly. However, it would be cool to be able to use the best practice container provided by Google.

I just got around to revisiting this. In looking at fly’s sample it looks like PUPPETEER_EXECUTABLE_PATH must be supplied by the platform. I’ve tried to implement puppeteer-core to avoid the need to pass system capabilities in docker run. I am getting the following error:

// puppeteer-core doesn't take into account PUPPETEER_* env variables.
    if (!launcher._isPuppeteerCore) {
        const executablePath = process.env.PUPPETEER_EXECUTABLE_PATH ||
            process.env.npm_config_puppeteer_executable_path ||
            process.env.npm_package_config_puppeteer_executable_path;
        if (executablePath) {
            const missingText = !fs.existsSync(executablePath)
                ? 'Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: ' +
                    executablePath
                : null;
            return { executablePath, missingText };
        }
    }

Puppeteer resolves the executable path as follows:

// puppeteer-core doesn't take into account PUPPETEER_* env variables.
    if (!launcher._isPuppeteerCore) {
        const executablePath = process.env.PUPPETEER_EXECUTABLE_PATH ||
            process.env.npm_config_puppeteer_executable_path ||
            process.env.npm_package_config_puppeteer_executable_path;
        if (executablePath) {
            const missingText = !fs.existsSync(executablePath)
                ? 'Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: ' +
                    executablePath
                : null;
            return { executablePath, missingText };
        }
    }

Do you have undocumented support for a browser executable by setting PUPPETEER_EXECUTABLE_PATH in your environments? If not you really need to add support for container capabilities like AWS did.

We don’t actually run containers, we extract your app into a VM with full root access. So the container capabilities in Docker don’t exactly apply!

Can you share your Dockerfile? Also can you share the exact logs you’re seeing when you start one?

Kurt, thanks for responding. Full docker file is below:

FROM node:16-slim

MAINTAINER Dorian Smiley, dsmiley@codestrap.me

WORKDIR /usr/app

# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# installs, work.
RUN apt-get update \
    && apt-get install -y wget gnupg \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
    && apt-get update \
    && apt-get install -y xvfb \
    && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
      --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

# If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise
# uncomment the following lines to have `dumb-init` as PID 1
# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_x86_64 /usr/local/bin/dumb-init
# RUN chmod +x /usr/local/bin/dumb-init
# ENTRYPOINT ["dumb-init", "--"]

# Uncomment to skip the chromium download when installing puppeteer. If you do,
# you'll need to launch puppeteer with:
#     browser.launch({executablePath: 'google-chrome-stable'})
# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
COPY package.json .
# COPY package-lock.json .
COPY index.html .
COPY favicon.ico .
COPY index.js .
COPY ./css ./css
COPY ./img ./img
COPY ./program ./program
COPY server.js .
COPY ./ssr ./ssr

# Install puppeteer so it's available in the container.
RUN npm install \
    # Add user so we don't need --no-sandbox.
    # same layer as npm install to keep re-chowned files from using up several hundred MBs more space
    && groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
    && mkdir -p /home/pptruser/Downloads \
    && chown -R pptruser:pptruser /home/pptruser \
    && chown -R pptruser:pptruser /usr/app/node_modules

# allow the pptruser write permissions to the container
RUN chown -R pptruser:pptruser /usr/app
RUN chmod 755 /usr/app

# Run everything after as non-privileged user.
USER pptruser

ENV PORT=80
ENV DISPLAY=1.0
ENV DEBUG='codestrap-website:*'

EXPOSE 3000
EXPOSE 80
EXPOSE 443

CMD ["google-chrome-stable"]

This is mostly taken from Google’s official docker guide for Puppeteer. The error is Unable to open X display which is described in this stack overflow issue. However xvfb requires --cap-add=SYS_ADMIN.

Also, the error I get when running the fly example app for puppeteer is:

Error: Could not find browser revision 756035. Run "npm install" or "yarn install" to download a browser binary.
    at ChromeLauncher.launch (/Users/doriansmiley/workspace/puppeteer-js-renderer/node_modules/puppeteer-core/lib/Launcher.js:59:23)
    at async /Users/doriansmiley/workspace/puppeteer-js-renderer/server.js:40:25

here’s the exact error message when I remove the added sys admin capabilities:

/usr/app/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:194
            reject(new Error([
                   ^

Error: Failed to launch the browser process!
[1210/043312.672580:FATAL:zygote_host_impl_linux.cc(117)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.
#0 0x55b715021259 base::debug::CollectStackTrace()
#1 0x55b714f8d9e3 base::debug::StackTrace::StackTrace()
#2 0x55b714f9e610 logging::LogMessage::~LogMessage()
#3 0x55b71368783b content::ZygoteHostImpl::Init()
#4 0x55b714f2d80c content::ContentMainRunnerImpl::Initialize()
#5 0x55b714f2bb09 content::RunContentProcess()
#6 0x55b714f2bc3d content::ContentMain()
#7 0x55b714f87c29 headless::HeadlessBrowserMain()
#8 0x55b714f8793a headless::HeadlessShellMain()
#9 0x55b711fb83e1 ChromeMain
#10 0x7fc585ed609b __libc_start_main
#11 0x55b711fb822a _start

Received signal 6
#0 0x55b715021259 base::debug::CollectStackTrace()
#1 0x55b714f8d9e3 base::debug::StackTrace::StackTrace()
#2 0x55b715020e00 base::debug::(anonymous namespace)::StackDumpSignalHandler()
#3 0x7fc587f5a730 (/lib/x86_64-linux-gnu/libpthread-2.28.so+0x1272f)
#4 0x7fc585ee97bb gsignal
#5 0x7fc585ed4535 abort
#6 0x55b715020425 base::debug::BreakDebugger()
#7 0x55b714f9ea37 logging::LogMessage::~LogMessage()
#8 0x55b71368783b content::ZygoteHostImpl::Init()
#9 0x55b714f2d80c content::ContentMainRunnerImpl::Initialize()
#10 0x55b714f2bb09 content::RunContentProcess()
#11 0x55b714f2bc3d content::ContentMain()
#12 0x55b714f87c29 headless::HeadlessBrowserMain()
#13 0x55b714f8793a headless::HeadlessShellMain()
#14 0x55b711fb83e1 ChromeMain
#15 0x7fc585ed609b __libc_start_main
#16 0x55b711fb822a _start
  r8: 0000000000000000  r9: 00007fff07a37520 r10: 0000000000000008 r11: 0000000000000246
 r12: 00007fff07a387f0 r13: 00007fff07a37780 r14: 00007fff07a38800 r15: aaaaaaaaaaaaaaaa
  di: 0000000000000002  si: 00007fff07a37520  bp: 00007fff07a37770  bx: 0000000000000006
  dx: 0000000000000000  ax: 0000000000000000  cx: 00007fc585ee97bb  sp: 00007fff07a37520
  ip: 00007fc585ee97bb efl: 0000000000000246 cgf: 002b000000000033 erf: 0000000000000000
 trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.


TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md

    at onClose (/usr/app/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:194:20)
    at Interface.<anonymous> (/usr/app/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:184:68)
    at Interface.emit (node:events:402:35)
    at Interface.close (node:readline:586:8)
    at Socket.onend (node:readline:277:10)
    at Socket.emit (node:events:402:35)
    at endReadableNT (node:internal/streams/readable:1343:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

Chrome is refusing to start without a sandbox. There’s a url after the error message that looks promising: puppeteer/troubleshooting.md at main · puppeteer/puppeteer · GitHub

Hey Michael, thanks for responding. " If you absolutely trust the content you open in Chrome, you can launch Chrome with the --no-sandbox argument". This is a website publishing service for a framework I created. I don’t trust the content passing through the service. That said I’ll add the no-sandbox option and see if it resolves the issue.

Disabling the sandbox is the recommended way to run on Heroku and Lambda sadly. But since you have root access to the machine on Fly you should be able to configure it using one of the other methods. We also have an upcoming api that lets you launch and destroy VMs very quickly which you could use to run untrusted code safely.

Adding '--no-sandbox', '--disable-setuid-sandbox' works but I don’t think I can use the service with those options enabled.

I marked this one as resolved. In the future, if there’s a better solution where I can keep the sandbox enabled let me know. Thanks for your help!

I think our replies got crossed :grinning_face_with_smiling_eyes: this will probably work right now if you run it from an ENTRYPOINT script before starting google-chrome-stable

hey Michael, I am having numerous issues migrating this project so it will run on Fly. I tried implementing an entry point script but I could not get it to work. Could you checkout the repo and see if you can get this setup to work on flow. codestrap/infra/fly/website at master · doriansmiley/codestrap · GitHub

I may have made a breakthrough. I just changed the command to CMD ["npm", "start"]. This removes the need for me to pass node -e \"cat server.js\" in docker run. I’m deploying now. Is there a way to specify port mappings in fly? I default to port 3000 and want to map 443:3000