I've created this container to mount google drive shares on my host system, without the need of installing this ocaml stuff.
It's pretty easy to use: authorize once (see First authorization below), then just start the container and wait for the successful mount. The token is persisted, so this is a one-time step.
Make sure to mount the config directory /root/.gdfuse so the token and config will persisted.
After that, just mount your volume mountpoint to the gdrive mountpoint inside the container like this: /mnt/path/where/you/want/your/gdrive:/data:shared.
It's loosely based on https://github.com/Patricol/dockerfiles-public/tree/master/alpine/gdrive.
currently tested on: x86_64, arm64, arm
In March 2023 - Docker informed me that they are going to remove my
organizations servercontainers and desktopcontainers unless
I'm upgrading to a pro plan.
I'm not going to do that. It's more of a professionally done hobby then a professional job I'm earning money with.
In order to avoid bad actors taking over my org. names and publishing potenial
backdoored containers, I'd recommend to switch over to my new github registry: ghcr.io/servercontainers.
Older versions shipped shared default OAuth client credentials, so you only
had to open the authentication link from the logs. Google retired the
copy-paste ("OOB") consent flow those relied on, and google-drive-ocamlfuse
0.9.x therefore ships no default credentials anymore — every user now
brings their own OAuth client. Once authorized, the refresh token (and your
client id/secret) are stored in the mounted /root/.gdfuse config and
survive restarts and upgrades, exactly like before.
- Create a project (any name) and enable the Google Drive API (APIs & Services → Library).
- OAuth consent screen: type External, fill the mandatory fields, and set the app to "In production" (publish it). In "Testing" mode Google expires refresh tokens after 7 days — you would have to re-authorize every week. Publishing an unverified personal app is fine; the consent page just shows a warning you can click through (Advanced → continue).
- Credentials → Create credentials → OAuth client ID → type "Desktop app"
— this gives you the
xxxx.apps.googleusercontent.comid and the secret.
Pass the credentials via the CLIENT_ID / CLIENT_SECRET environment
variables (or -id/-secret below). A fresh Desktop client uses a
loopback redirect: google-drive-ocamlfuse listens on 127.0.0.1:8080 and
the consent page redirects there.
On a machine with a browser, simply start the container with the env variables set, open the URL from the logs, and grant access.
On a headless machine, tunnel your local browser to the container's loopback listener:
# terminal 1 - on your desktop
ssh -L 8080:127.0.0.1:8080 user@headless-host
# terminal 2 - on the headless host (one-shot; then start normally)
docker run --rm -p 127.0.0.1:8080:8080 \
-v /path/to/config:/root/.gdfuse \
--entrypoint google-drive-ocamlfuse \
ghcr.io/servercontainers/google-drive-ocamlfuse \
-id xxxx.apps.googleusercontent.com -secret your-secret -browser echo
-browser echo prints the consent URL instead of trying to open a browser —
open it on your desktop, grant access, and the redirect flows through the
tunnel back into the container, which stores the token and exits. Then start
the container normally.
If Google answers with invalid_grant, the code expired — just run the
authorization again.
You can specify DOCKER_REGISTRY environment variable (for example my.registry.tld)
and use the build script to build the main container and it's variants for x86_64, arm64 and arm
You'll find all images tagged like a3.15.0-g0.7.23 which means a<alpine version>-g<google-drive-ocamlfuse version>.
This way you can pin your installation/configuration to a certian version. or easily roll back if you experience any problems
(don't forget to open a issue in that case ;D).
To build a latest tag run ./build.sh release
- 2026-08-30
- fixed startup crash-loop with google-drive-ocamlfuse 0.9.x (obsolete
-fflag - the process stays in foreground by itself since the 0.8.0 dependency bump) - new
CLIENT_ID/CLIENT_SECRETenvironment variables (0.9.x requires own OAuth client credentials for new setups) - entrypoint-level smoke test in CI that catches launcher/binary option drift
- documented the new first-authorization flow (own OAuth client, loopback redirect, headless via ssh tunnel)
- documented host prerequisites and mount propagation (
:shared/:rslave) for consuming the mount from other containers with independent lifecycles
- fixed startup crash-loop with google-drive-ocamlfuse 0.9.x (obsolete
- 2023-03-20
- github action to build container
- implemented ghcr.io as new registry
- 2023-03-18
- switched from docker hub to a build-yourself container
- 2022-12-27
- fixed broken build (opam depext etc.)
- fixed url display problem in custom
xdg-open
- 2022-01-08
- better build script
- improved readme
- version pinning
- custom user / group creation - for better mapping
- 2021-01-03
- added bash to fix scripts and startup
- 2020-11-20
- removed unsupported arm 32 bit builds
- 2020-11-18
- initial creation
- up-to-date build
- multiarch support
-
device/dev/fuse
-
cap-addmknodsys_admin
-
security-optapparmor:unconfined
-
PUID
- optional
- user id to map the google drive directory and files
- default value:
0(root)
-
PGID
- optional
- group id to map the google drive directory and files
- default value:
0(root)
-
MOUNT_OPTS
- optional
- additional mount opts
- no default value
-
CLIENT_ID / CLIENT_SECRET
- required for NEW setups (empty config volume) since google-drive-ocamlfuse 0.9.x - it no longer ships default OAuth client credentials
- create your own OAuth client (Desktop app) in the Google Cloud console and pass id + secret here for the first authorization
- existing config volumes keep working without these - the values are stored in the config after the first authorization
-
GROUP_groupname
- optional
- value will be
gid - example:
GROUP_devops=1500will create groupdevopswith id1500
-
ACCOUNT_username
- optional
- multiple variables/accounts possible
- adds a new user account with the given username and the user id the env value
- example
ACCOUNT_foo: 1001will create userfoowith uid1001 - see
docker-compose.ymluserfoofor an example how it's used/configured.
- example
-
GROUPS_username
- optional
- additional groups for the user
- to create groups look at
GROUP_groupnameor mount/inject /etc/groups file (can cause problems) - the
usernamepart must match to a specifiedACCOUNT_usernameenvironment variable - one or more groups to add seperated by a
, - example:
GROUPS_foo=musican,devops
The whole point of mounting to a host path (:shared) is that other
containers — a samba server, a media server, a backup job — can consume the
Google Drive mount by binding the same host path. For that to work reliably,
and for both containers to keep independent lifecycles (start / stop /
restart either one without touching the other), three things must be true:
- The host must have the fuse module loaded (
modprobe fuse, and make it persistent for boot — a missing module fails container creation witherror gathering device information ... /dev/fuse). - The host path's parent mount must allow propagation. Docker rejects
:shared/:rslavebinds with... is not a shared or slave mountwhen the underlying filesystem is mountedprivate. Most systemd distros mount/shared by default; minimal/init-based systems often don't — fix withmount --make-rshared /your/mountpoint(and persist it in your boot sequence, before containers start). - This container binds
:shared, consumers bind:rslave.:shared(here) pushes the FUSE mount out to the host.:rslave(consumer) makes the consumer follow mount and unmount events on that path. That's what buys the independence: restart this container and the fresh mount propagates into the running consumer automatically; while it's down, the consumer just sees an empty directory instead of a stale or broken mount. A plain bind (no propagation flag) only sees whatever was mounted when the consumer started — after a gdrive restart it silently serves an empty dir until you restart the consumer too.
# this container
volumes:
- /srv/gdrive:/data:shared
# any consumer container
volumes:
- /srv/gdrive:/consumed/path:rslave
Since it was difficult to integrate with my samba system (permissions problem) I've enforced the user root.
After that it worked nicely.
You can use the folder also inside your Samba configuration (servercontainers/samba)
If you want to share the folder using samba, make sure your user has the same UID/GID.
Default is set to root which has UID: 0 and GID: 0.
In order to use it properly with samba, you can enforce user and group root or use the UID/GID of the user you want to use it with.
SAMBA_VOLUME_CONFIG_gdrive: "[GDrive]; path = /shares/gdrive; valid users = alice; guest ok = no; read only = no; browseable = yes; force user = root; force group = root"
and bind the host path with propagation (see the section above), so samba survives gdrive restarts without a restart of its own:
volumes:
- /srv/gdrive:/shares/gdrive:rslave
[GDrive]
path = /shares/gdrive
valid users = alice
guest ok = no
read only = no
browseable = yes
force user = root
force group = root