Skip to Content
⚠️ Dockflow is currently under development. Bugs may occur. Please report any issues on GitHub.
ConfigurationFile Uploads

File Uploads

Transfer local files or directories to your servers before each deployment. Useful for configuration files that services reference via bind mounts.

Minimal example

# .dockflow/config.yml uploads: - src: .dockflow/docker/mosquitto/ dest: /opt/dockflow/mqtt/

Then reference the absolute path in your compose file:

# docker-compose.yml services: mqtt: volumes: - /opt/dockflow/mqtt/mosquitto.conf:/mosquitto/config/mosquitto.conf

Options

FieldTypeDescriptionRequired
srcstringLocal path relative to the project root (file or directory)Yes
deststringAbsolute destination path on the remote serverYes
servicestring | string[]Service(s) this upload belongs to. When deploying with --only, only uploads matching a targeted service are transferred. Uploads without this field are always transferred.No
permissionsstringFile permissions applied after upload, in octal notation (e.g. "640", "755").No
ownerstringFile owner applied after upload, in "user" or "user:group" format (e.g. "mosquitto", "www-data:www-data"). Requires the deploy user to have sudo or sufficient privileges.No
excludestring[]Glob patterns or paths to skip during directory upload (e.g. ".git", "*.md", "node_modules"). Ignored for single-file uploads.No
compressbooleanCompress the archive during transfer (default: true). Set to false for directories full of already-compressed files (JARs, ZIPs, images) to reduce CPU overhead without affecting transfer size.No

dest must start with /. If src is a directory, dest is used as the destination directory and the relative structure is preserved.

How it works

Permission check

Before building, dockflow verifies that the deploy user has write access to all upload destinations on every node. If a destination doesn’t exist yet, its nearest existing parent directory is checked instead. The deploy fails immediately with a clear fix command if any permission is missing.

Build

Docker images are built while the permission check is happening.

Transfer to all nodes

Files are transferred to the manager and all worker nodes in parallel after the build, so bind mounts work regardless of which node runs the container.

Pre-deploy hook

The pre-deploy hook runs after uploads complete — meaning it can reference the uploaded files directly (e.g. systemctl daemon-reload after uploading a systemd unit).

Deploy

The stack is deployed after all transfers and the pre-deploy hook complete.

Nunjucks rendering

Files inside .dockflow/ are automatically rendered through Nunjucks  before being uploaded. This means you can use any template variable in your uploaded files:

# .dockflow/services/myapp.service [Service] Environment="DB_HOST={{ current.env.db_host }}" Environment="DB_PASSWORD={{ current.env.db_password }}" ExecStart=/usr/bin/myapp

Files outside .dockflow/ are transferred as-is without rendering.

Single file vs directory

uploads: # Single file → transferred to exact dest path - src: .dockflow/docker/nginx/nginx.conf dest: /opt/dockflow/nginx/nginx.conf # Directory → dest is the target directory, structure preserved - src: .dockflow/docker/mosquitto/ dest: /opt/dockflow/mqtt/ # result: /opt/dockflow/mqtt/mosquitto.conf # /opt/dockflow/mqtt/certs/ca.crt # etc.

If dest ends with / for a single file, the filename is preserved:

uploads: - src: .dockflow/docker/mosquitto.conf dest: /opt/dockflow/mqtt/ # → /opt/dockflow/mqtt/mosquitto.conf

Files are uploaded on every deployment, even if unchanged. Avoid putting large files in uploads — bake them into the Docker image instead.

Files are created with the server’s default umask. Use permissions and owner to enforce specific access control after upload.

uploads: - src: .dockflow/docker/mosquitto/passwd dest: /opt/dockflow/mqtt/passwd permissions: "640" owner: "mosquitto:mosquitto"

owner requires the deploy user to have sudo rights for chown. Without it, the deployment will fail with a clear message showing the exact command to run manually or the sudoers rule to add.

To grant the right permanently on the server:

echo 'dockflow ALL=(ALL) NOPASSWD: /bin/chown * /opt/dockflow/*' >> /etc/sudoers.d/dockflow

Restrict the path to the directories you actually use to limit the privilege surface.

Permission setup

Before the first deployment, the deploy user must have write access to each destination. Dockflow detects this automatically and prints the exact commands to run:

Upload permission check failed: main_server: .dockflow/services/myapp.service: cannot create /etc/systemd/system/myapp.service (nearest existing parent: /etc/systemd/system) → Run once on each failing server as root: touch '/etc/systemd/system/myapp.service' && chown dockflow: '/etc/systemd/system/myapp.service'

For directories:

mkdir -p '/opt/dockflow/mqtt' && chown -R dockflow: '/opt/dockflow/mqtt'

Scoping uploads to a service

When using --only to deploy a single service, uploads without a service field still run. Use the service field to restrict an upload to specific services:

uploads: - src: .dockflow/docker/mosquitto/ dest: /opt/dockflow/mqtt/ service: mqtt # only transferred when deploying the mqtt service - src: .dockflow/docker/prometheus.yml dest: /opt/dockflow/monitoring/prometheus.yml service: [prometheus, grafana] # transferred when either service is targeted - src: .dockflow/shared.conf dest: /opt/dockflow/shared.conf # no service field → always transferred

Full example

# .dockflow/config.yml project_name: my-app uploads: - src: .dockflow/docker/mosquitto/ dest: /opt/dockflow/mqtt/ service: mqtt - src: .dockflow/docker/prometheus.yml dest: /opt/dockflow/monitoring/prometheus.yml service: prometheus
# docker-compose.yml services: mqtt: image: eclipse-mosquitto:2 volumes: - /opt/dockflow/mqtt/mosquitto.conf:/mosquitto/config/mosquitto.conf - /opt/dockflow/mqtt/passwd:/mosquitto/config/passwd prometheus: image: prom/prometheus volumes: - /opt/dockflow/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml

Upload-only projects

Some projects have no Docker services — they only transfer files to a server. Use no_services: true to declare this explicitly:

# .dockflow/config.yml project_name: my-project no_services: true uploads: - src: ./dist/ dest: /var/www/my-project/

With no_services: true:

  • Docker build and compose deploy are skipped entirely
  • Commands that require a running stack (dockflow app logs, dockflow app status, rollback, etc.) return a clear error instead of failing silently
  • Pre/post-deploy hooks still run

Without no_services: true, a missing docker-compose.yml is treated as a configuration error and the deployment fails. Only set this flag when the project genuinely has no Docker services — not as a workaround for a misconfigured compose path.

See also

  • Hooks — use pre-deploy to reload services after upload (e.g. systemctl daemon-reload)
  • Templates — full reference for available template variables