WHMCS + Docker: hands off provisioning and control
Provisioning a container from a WHMCS order sounds simple until you’re the one copying client IDs, checking domains, running SSH commands, and replying to “is it ready yet?” all day. One missed step and you’ve got the wrong user mapped to the wrong service. Fun.
This is the kind of mess that hits hosting business owners first, but support leads and DevOps folks end up carrying it too. With WHMCS Docker automation, those “create / start / stop / logs / reset password” requests get handled the same way every time, which means fewer tickets bouncing around and fewer human mistakes.
Below you’ll see exactly what the n8n workflow is doing, what results you can expect, and what you’ll need to run it reliably in production.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: WHMCS + Docker: hands off provisioning and control
flowchart LR
subgraph sg0["Flow 1"]
direction LR
n0@{ icon: "mdi:swap-horizontal", form: "rounded", label: "If", pos: "b", h: 48 }
n1@{ icon: "mdi:swap-vertical", form: "rounded", label: "Parametrs", pos: "b", h: 48 }
n2["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/webhook.dark.svg' width='40' height='40' /></div><br/>API"]
n3["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/webhook.dark.svg' width='40' height='40' /></div><br/>422-Invalid server domain"]
n4["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Code1"]
n5@{ icon: "mdi:cog", form: "rounded", label: "SSH", pos: "b", h: 48 }
n6@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Container Actions", pos: "b", h: 48 }
n7@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Service Actions", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/webhook.dark.svg' width='40' height='40' /></div><br/>API answer"]
n9@{ icon: "mdi:swap-vertical", form: "rounded", label: "Inspect", pos: "b", h: 48 }
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Stat", pos: "b", h: 48 }
n11@{ icon: "mdi:swap-vertical", form: "rounded", label: "Start", pos: "b", h: 48 }
n12@{ icon: "mdi:swap-vertical", form: "rounded", label: "Stop", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-vertical", form: "rounded", label: "Test Connection1", pos: "b", h: 48 }
n14@{ icon: "mdi:swap-vertical", form: "rounded", label: "Deploy", pos: "b", h: 48 }
n15@{ icon: "mdi:swap-vertical", form: "rounded", label: "Suspend", pos: "b", h: 48 }
n16@{ icon: "mdi:swap-vertical", form: "rounded", label: "Terminated", pos: "b", h: 48 }
n17@{ icon: "mdi:swap-vertical", form: "rounded", label: "Unsuspend", pos: "b", h: 48 }
n18@{ icon: "mdi:swap-vertical", form: "rounded", label: "Mount Disk", pos: "b", h: 48 }
n19@{ icon: "mdi:swap-vertical", form: "rounded", label: "Unmount Disk", pos: "b", h: 48 }
n20@{ icon: "mdi:swap-vertical", form: "rounded", label: "Log", pos: "b", h: 48 }
n21@{ icon: "mdi:swap-vertical", form: "rounded", label: "ChangePackage", pos: "b", h: 48 }
n22@{ icon: "mdi:swap-vertical", form: "rounded", label: "Deploy-docker-compose", pos: "b", h: 48 }
n23@{ icon: "mdi:swap-vertical", form: "rounded", label: "Version", pos: "b", h: 48 }
n24@{ icon: "mdi:swap-horizontal", form: "rounded", label: "If1", pos: "b", h: 48 }
n25@{ icon: "mdi:swap-vertical", form: "rounded", label: "nginx", pos: "b", h: 48 }
n26@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Container Stat", pos: "b", h: 48 }
n27@{ icon: "mdi:swap-vertical", form: "rounded", label: "GET ACL", pos: "b", h: 48 }
n28@{ icon: "mdi:swap-vertical", form: "rounded", label: "SET ACL", pos: "b", h: 48 }
n29@{ icon: "mdi:swap-vertical", form: "rounded", label: "GET NET", pos: "b", h: 48 }
n30@{ icon: "mdi:swap-vertical", form: "rounded", label: "Change Password", pos: "b", h: 48 }
n31@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Grafana", pos: "b", h: 48 }
n0 --> n26
n0 --> n6
n0 --> n31
n0 --> n24
n0 --> n3
n2 --> n1
n24 --> n25
n24 --> n7
n20 --> n5
n5 --> n4
n10 --> n5
n12 --> n5
n4 --> n8
n11 --> n5
n25 --> n22
n14 --> n5
n27 --> n5
n29 --> n5
n31 --> n23
n31 --> n30
n9 --> n5
n28 --> n5
n15 --> n5
n23 --> n5
n1 --> n0
n17 --> n5
n18 --> n5
n16 --> n5
n19 --> n5
n21 --> n5
n26 --> n9
n26 --> n10
n26 --> n20
n30 --> n5
n7 --> n13
n7 --> n14
n7 --> n15
n7 --> n17
n7 --> n16
n7 --> n21
n13 --> n5
n6 --> n11
n6 --> n12
n6 --> n18
n6 --> n19
n6 --> n27
n6 --> n28
n6 --> n29
n22 --> n7
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0,n6,n7,n24,n26,n31 decision
class n2,n3,n8 api
class n4 code
classDef customIcon fill:none,stroke:none
class n2,n3,n4,n8 customIcon
The Problem: WHMCS Requests Still Turn Into Manual SSH Work
In a typical setup, WHMCS is where customers click buttons and open tickets, but the actual work still happens somewhere else. Somebody checks the order details, confirms the request is valid, logs into a server, runs the right Docker commands, maybe edits a compose file, and then replies back with “done.” It’s not just slow. It’s mentally expensive, because you’re context-switching between billing, infrastructure, and support conversations. When volume increases, the failure mode is predictable: missed steps, inconsistent responses, and “quick fixes” that turn into untracked exceptions.
The friction compounds.
- Support staff end up acting like a command-line proxy, which blocks them from clearing the rest of the queue.
- Provisioning varies by who is on shift, so you get inconsistent container configs and uneven troubleshooting later.
- Simple requests like “send logs” or “reset admin password” turn into 10–15 minutes of back-and-forth and manual checks.
- One wrong domain or misrouted request can hit the wrong host, which is a security problem, not a nuisance.
The Solution: WHMCS Triggers n8n, n8n Controls Docker Safely
This workflow turns WHMCS module actions into an API-driven backend that actually performs the work. It starts with an incoming webhook (protected with Basic Auth), receives a command from your WHMCS module, and sets the required variables for the request. Before anything touches your infrastructure, it runs a domain match check, so requests only execute against the server domain you expect. After that, the workflow routes the request to the correct action, builds any required configuration (like Docker Compose and Nginx templates), then connects over SSH to your Docker host to execute predefined scripts. Finally, it parses the SSH output into a clean JSON or string response and sends a consistent “success or failure” payload back to WHMCS.
The workflow starts when WHMCS calls the webhook with a specific command. n8n validates the target domain, routes the command (service actions, container actions, Grafana actions), and executes the matching SSH script. The reply comes back immediately in a format your module can show to staff or customers, so nobody has to translate terminal output into support English.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say your team handles 25 common requests a week: provisions, restarts, log pulls, and the occasional password reset. Manually, even a “quick” request is often about 10 minutes end-to-end once you include WHMCS lookup, SSH, verification, and the reply, which is roughly 4 hours a week. With this workflow, support triggers the action from WHMCS, n8n runs the SSH script, and the response comes back in a consistent format in a minute or two. That’s several hours back, and it’s the same process on Monday morning and Friday night.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- WHMCS to trigger module actions and requests.
- Docker host server where containers and disks live.
- Basic Auth credentials (create in n8n Credentials for the webhook).
- SSH credentials (set up in n8n to access the Docker server).
Skill level: Intermediate. You’ll be comfortable creating credentials, editing a few template parameters, and verifying SSH scripts on your server.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A WHMCS request triggers a secure webhook. Your WHMCS Docker module calls the n8n webhook with Basic Auth and a specific command (like provision, suspend, start, fetch logs, or reset password).
The workflow validates and prepares the request. n8n sets the incoming variables, then runs a domain match check so only approved server domains can execute actions. If it fails, it returns a 422 response right away.
Routing selects the right action path. Switch nodes direct the request into container actions, service operations, or Grafana-related actions. For service operations, the workflow can generate Nginx templates and build a Docker Compose file before running anything.
SSH executes scripts, then n8n returns clean output. The workflow runs predefined Bash scripts on the Docker host, parses the output into a predictable response, and sends it back to WHMCS so your team sees clear results instead of raw command output.
You can easily modify the compose and Nginx templates to match your stack, then keep the same WHMCS-facing commands. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Webhook Trigger
Set up the entry point that receives orchestration requests and returns responses through the response nodes.
- Add and configure Incoming Webhook as the trigger.
- Set Path to
docker-grafanaand Response Mode toresponseNode. - Enable Authentication as
basicAuthand allow HTTP Methods to includePOST. - Credential Required: Connect your httpBasicAuth credentials in Incoming Webhook.
server_domain, domain, and command in the JSON body, since downstream routing depends on these fields.Step 2: Connect SSH Access
All actions are executed remotely via SSH, so configure the command runner before building the logic.
- Open Run SSH Command and set Working Directory to
=/. - Set Command to
{{ $json.sh }}so it executes the script generated by upstream nodes. - Credential Required: Connect your sshPassword credentials in Run SSH Command.
Step 3: Configure Input Variables and Domain Validation
Define static defaults and validate requests before any SSH operations run.
- In Input Variables, set the following assignments: server_domain to
d01-test.uuq.pl, clients_dir to/opt/docker/clients, mount_dir to/mnt, screen_left to{{, and screen_right to}}. - In Domain Match Check, compare
{{ $json.server_domain }}with{{ $('Incoming Webhook').item.json.body.server_domain }}to validate the caller domain. - Connect the false branch of Domain Match Check to 422 Invalid Domain Reply to return the JSON error response.
{"status":"error","error":"Invalid server domain"}, which is useful for early security checks.Step 4: Route Requests in Parallel
Split execution into specialized branches so container operations, Grafana actions, and service operations can run independently.
- Ensure Input Variables connects to Domain Match Check.
- Configure Domain Match Check so the true branch fans out to the four routers in parallel.
- Domain Match Check outputs to both Container Info Router and Container Command Router and Grafana Actions and Service Ops Check in parallel.
- In Container Info Router, ensure command routes include
container_information_inspect,container_information_stats, andcontainer_log. - In Container Command Router, ensure command routes include
container_start,container_stop,container_mount_disk,container_unmount_disk,container_get_acl,container_set_acl, andcontainer_get_net.
Step 5: Configure Container Operations via SSH
These nodes build the shell scripts that are sent to the server and parsed before responding.
- Confirm Inspect Container, Collect Stats, and Fetch Logs each build a sh script based on
{{ $('Incoming Webhook').item.json.body.domain }}and values from Input Variables. - Confirm Start Container, Stop Container, Attach Disk, and Detach Disk generate the appropriate shell scripts for Docker and disk operations.
- Confirm Retrieve ACL, Update ACL, and Network Usage generate scripts that reference
{{ $('Input Variables').item.json.clients_dir }}and{{ $('Incoming Webhook').item.json.body.domain }}. - Route all of these set nodes into Run SSH Command so the scripts execute on the server.
- Keep Parse SSH Output after Run SSH Command to normalize responses and then send them through Webhook Success Reply.
Step 6: Configure Service Provisioning and Lifecycle Actions
This branch prepares Nginx templates, builds docker-compose files, and performs create/suspend/resize operations.
- In Service Ops Check, ensure the conditions cover
create,change_package, andunsuspend. - In Nginx Templates, set main to
=and main_location to=proxy_pass_header Server; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host;. - In Build Compose File, keep the docker-compose assignment as the templated string that references
{{ $('Incoming Webhook').item.json.body.domain }},{{ $('Incoming Webhook').item.json.body.username }}, and{{ $('Incoming Webhook').item.json.body.password }}. - Verify Service Command Router routes commands
test_connection,create,suspend,unsuspend,terminate, andchange_packageinto the appropriate action nodes. - Ensure Provision Service, Restore Service, and Resize Package use base64-encoded templates such as
{{ JSON.stringify($('Build Compose File').item.json['docker-compose']).base64Encode() }}and{{ JSON.stringify($('Nginx Templates').item.json['main_location']).base64Encode() }}.
Step 7: Configure Grafana-Specific Actions and Responses
Grafana-specific commands route to version checks and password resets, and all successful outputs return through the webhook response.
- In Grafana Actions, confirm the rules map
app_versionto Read App Version andchange_passwordto Reset Admin Password. - Make sure both Read App Version and Reset Admin Password pass to Run SSH Command for execution.
- Keep Webhook Success Reply configured with Respond With set to
allIncomingItemsso it returns the parsed JSON from Parse SSH Output.
Step 8: Test and Activate Your Workflow
Run a controlled test to confirm routing, SSH execution, and webhook responses.
- Use Execute Workflow and send a POST request to the Incoming Webhook URL with valid basic auth credentials and a JSON payload containing
server_domain,domain, andcommand. - Verify success responses return through Webhook Success Reply and include
statusandmessagefrom Parse SSH Output. - Trigger a mismatched domain to confirm 422 Invalid Domain Reply returns the error JSON and HTTP 422 status.
- When tests pass, toggle the workflow to Active for production use.
Common Gotchas
- WHMCS webhook credentials can expire or get changed during module updates. If requests suddenly fail, check the Basic Auth credential in n8n and the module’s saved endpoint settings first.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
About an hour if SSH and WHMCS are already in place.
No. You’ll mainly configure credentials, edit a few parameters, and test commands safely.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in your server costs (VPS/hosting) since Docker runs on your infrastructure.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and honestly you should. Update the values in the “Input Variables” and “Parameters” settings (like server_domain, clients_dir, and mount_dir), then adjust the “Build Compose File” and “Nginx Templates” blocks to match your stack. Common tweaks include adding environment variables for the app, changing persistent volume mounts, and modifying the Nginx location rules for headers and auth. Keep the routing the same so WHMCS still sends familiar commands.
Most of the time it’s Basic Auth mismatches or the domain check blocking the call. Confirm the WHMCS module is calling the correct webhook URL, then verify the saved Basic Auth credentials in n8n match what WHMCS sends. Also check the “Domain Match Check” settings, because a small domain mismatch can force the 422 invalid domain reply even when everything else is fine.
If you self-host n8n, there’s no hard execution cap (it depends on your server). On n8n Cloud, the limit depends on your plan, but most small hosts can run this comfortably because each request is just a webhook + a short SSH execution. The real constraint is your Docker host capacity and how heavy your scripts are, especially for provisioning and disk operations.
Usually, yes, because this workflow lives close to infrastructure and needs branching, routing, and SSH execution. Zapier and Make can trigger webhooks, but they’re not a great fit for server-side command execution and multi-path service logic unless you bolt on extra services. n8n also gives you a self-host option, which matters when you’re running lots of operational actions and don’t want to pay per task. If your needs are only “create a ticket and send a Slack message,” those tools are fine. For “WHMCS pressed a button, now Docker must do the right thing,” n8n is the more practical choice. Talk to an automation expert if you want a second opinion on your setup.
Once this is in place, routine WHMCS requests stop turning into little emergencies. The workflow runs the same checks, returns the same clean outputs, and gives your team breathing room again.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.