WHMCS + Docker: provisioning and suspends handled
WHMCS says a service is “suspended,” but the container is still happily running. Or provisioning fails, and now you’re hopping between WHMCS, SSH, and Docker commands trying to figure out what actually happened.
This WHMCS Docker automation hits hosting operators hardest, but support leads and DevOps folks feel it too. The outcome is simple: service actions in WHMCS consistently trigger the right Docker actions on the server, without manual logins.
You’ll see how the workflow receives commands, validates requests, runs the correct SSH scripts, and returns a clean response back to WHMCS so your team can move on.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: WHMCS + Docker: provisioning and suspends handled
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@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Container Stats", pos: "b", h: 48 }
n9["<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"]
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Inspect", pos: "b", h: 48 }
n11@{ icon: "mdi:swap-vertical", form: "rounded", label: "Stat", pos: "b", h: 48 }
n12@{ icon: "mdi:swap-vertical", form: "rounded", label: "Start", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-vertical", form: "rounded", label: "Stop", pos: "b", h: 48 }
n14@{ icon: "mdi:swap-vertical", form: "rounded", label: "Test Connection1", pos: "b", h: 48 }
n15@{ icon: "mdi:swap-vertical", form: "rounded", label: "Deploy", pos: "b", h: 48 }
n16@{ icon: "mdi:swap-vertical", form: "rounded", label: "Suspend", pos: "b", h: 48 }
n17@{ icon: "mdi:swap-vertical", form: "rounded", label: "Terminated", pos: "b", h: 48 }
n18@{ icon: "mdi:swap-vertical", form: "rounded", label: "Unsuspend", pos: "b", h: 48 }
n19@{ icon: "mdi:swap-vertical", form: "rounded", label: "Mount Disk", pos: "b", h: 48 }
n20@{ icon: "mdi:swap-vertical", form: "rounded", label: "Unmount Disk", pos: "b", h: 48 }
n21@{ icon: "mdi:swap-vertical", form: "rounded", label: "Log", pos: "b", h: 48 }
n22@{ icon: "mdi:swap-vertical", form: "rounded", label: "ChangePackage", pos: "b", h: 48 }
n23@{ icon: "mdi:swap-horizontal", form: "rounded", label: "n8n", pos: "b", h: 48 }
n24@{ icon: "mdi:swap-vertical", form: "rounded", label: "Version", pos: "b", h: 48 }
n25@{ icon: "mdi:swap-vertical", form: "rounded", label: "Users", pos: "b", h: 48 }
n26@{ icon: "mdi:swap-vertical", form: "rounded", label: "Change Password", pos: "b", h: 48 }
n27@{ icon: "mdi:swap-horizontal", form: "rounded", label: "If1", pos: "b", h: 48 }
n28@{ icon: "mdi:swap-vertical", form: "rounded", label: "nginx", pos: "b", h: 48 }
n29@{ icon: "mdi:swap-vertical", form: "rounded", label: "Deploy-docker-compose", pos: "b", h: 48 }
n30@{ icon: "mdi:swap-vertical", form: "rounded", label: "GET ACL", pos: "b", h: 48 }
n31@{ icon: "mdi:swap-vertical", form: "rounded", label: "SET ACL", pos: "b", h: 48 }
n32@{ icon: "mdi:swap-vertical", form: "rounded", label: "GET NET", pos: "b", h: 48 }
n0 --> n8
n0 --> n6
n0 --> n23
n0 --> n27
n0 --> n3
n2 --> n1
n27 --> n28
n27 --> n7
n21 --> n5
n5 --> n4
n23 --> n24
n23 --> n25
n23 --> n26
n11 --> n5
n13 --> n5
n4 --> n9
n12 --> n5
n25 --> n5
n28 --> n29
n15 --> n5
n30 --> n5
n32 --> n5
n10 --> n5
n31 --> n5
n16 --> n5
n24 --> n5
n1 --> n0
n18 --> n5
n19 --> n5
n17 --> n5
n20 --> n5
n22 --> n5
n26 --> n5
n8 --> n10
n8 --> n11
n8 --> n21
n7 --> n14
n7 --> n15
n7 --> n16
n7 --> n18
n7 --> n17
n7 --> n22
n14 --> n5
n6 --> n12
n6 --> n13
n6 --> n19
n6 --> n20
n6 --> n30
n6 --> n31
n6 --> n32
n29 --> 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,n8,n23,n27 decision
class n2,n3,n9 api
class n4 code
classDef customIcon fill:none,stroke:none
class n2,n3,n4,n9 customIcon
The Problem: WHMCS service actions don’t reliably match Docker reality
When WHMCS is your source of truth but Docker is where the service actually lives, tiny mismatches become daily fires. A “provision” button in WHMCS may require building a compose file, preparing Nginx config, setting ACL, mounting storage, and starting the container. Do that by hand and it’s slow. Do it under pressure and it’s error-prone. The worst part is the mental load: your team has to remember the right command order, the right server, and the right client directory every time. Miss one piece and you’re debugging at the exact moment the customer is watching.
It adds up fast. Here’s where it usually breaks down in real teams.
- Provisioning becomes a “tribal knowledge” checklist that only one or two people can execute confidently.
- Suspends and restores drift because WHMCS updates instantly, while Docker actions depend on someone running the right commands.
- Support asks for logs and stats, but getting them means interrupting an engineer to SSH into production.
- One wrong domain, mount path, or client directory can create a mess that takes about an hour to unwind.
The Solution: WHMCS triggers Docker actions through an n8n API backend
This workflow turns n8n into a controlled “service actions API” for your WHMCS Docker module. It starts with an incoming webhook call (from WHMCS/WISECP), then assigns and normalizes the request parameters so every command has the same shape. Next, it validates the domain to reduce accidental execution against the wrong target, and routes the request to the right action group (service actions, container actions, or app actions). From there, the workflow generates the appropriate Bash script content and executes it over SSH on a server that already has Docker installed. Finally, it parses the SSH output into a clean response and sends it back to WHMCS, so the billing platform can record a clear success or failure instead of a vague “module command error.”
The workflow starts when WHMCS calls the n8n webhook with a specific command like provision, suspend, restore, terminate, or fetch logs. n8n routes that command, runs the matching SSH-backed script, and responds with structured output (JSON or a readable string) for your module to consume. No tab-hopping.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you handle about 20 service actions a week across provision, suspend, restore, and “please grab logs.” Manually, each one often means 10 minutes to find the right server, SSH in, run Docker commands, and paste results back into a ticket, so you’re burning roughly 3 hours weekly. With this workflow, the WHMCS action triggers the webhook in seconds, the SSH script runs in a minute or two, and the response comes back automatically. You still review edge cases, but the routine work mostly disappears.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- WHMCS to trigger module service actions.
- Docker server (Linux) to run containers and compose stacks.
- SSH credentials (create in n8n credentials manager)
- Webhook Basic Auth (create in n8n, used by WHMCS)
Skill level: Intermediate. You’ll connect credentials, edit a few parameters (domain and directories), and be comfortable validating SSH access.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
WHMCS calls the webhook API. When a customer orders a service, misses a payment, or support triggers an action, WHMCS hits the “Incoming API Webhook” endpoint in n8n using Basic Auth.
Parameters get normalized and checked. n8n assigns values like server_domain, client directories, and mount defaults, then “Validate Domain” blocks bad targets and sends a 422 reply when something is off.
The workflow routes to the right command. A set of routers (service, container, and app) decide what should happen next: provision vs. suspend, start vs. stop, fetch logs vs. inspect stats, and so on.
SSH runs the actual Docker work. The “Remote SSH Runner” executes the generated scripts on your Docker host, then “Parse SSH Result” formats the output before “Webhook Response” returns it to WHMCS.
You can easily modify the Docker Compose template and Nginx config generation to match your product plans and customer requirements. 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 API requests for Docker automation tasks.
- Add and open Incoming API Webhook.
- Set Path to
docker-n8n. - Set Response Mode to
responseNode. - Enable Authentication with
basicAuthand keep Multiple Methods enabled. - Credential Required: Connect your
httpBasicAuthcredentials.
Step 2: Define Global Parameters and Domain Validation
Initialize defaults and validate the incoming server domain before routing commands.
- Open Assign Parameters and set server_domain to
d01-test.uuq.pl. - Set clients_dir to
/opt/docker/clientsand mount_dir to/mnt. - Set screen_left to
{{and screen_right to}}. - Configure Validate Domain to compare
{{$json.server_domain}}with{{$('Incoming API Webhook').item.json.body.server_domain}}. - Ensure the false path of Validate Domain connects to 422 Invalid Domain Reply and returns the JSON body defined in that node.
server_domain doesn’t match Assign Parameters, the workflow returns the 422 Invalid Domain Reply response and no commands will run.
Step 3: Configure Parallel Routing for Command Types
After validation, the workflow routes commands in parallel based on the incoming command value.
- Confirm Assign Parameters connects to Validate Domain.
- Ensure Validate Domain outputs to Container Info Router, Container Command Router, App Command Router, and Route Service Setup in parallel.
- In Container Info Router, confirm rules use
{{$('Incoming API Webhook').item.json.body.command}}forcontainer_information_inspect,container_information_stats, andcontainer_log. - In Container Command Router, confirm rules use
{{$('Incoming API Webhook').item.json.body.command}}forcontainer_start,container_stop,container_mount_disk,container_unmount_disk,container_get_acl,container_set_acl, andcontainer_get_net. - In App Command Router, confirm rules use
{{$('Incoming API Webhook').item.json.body.command}}forapp_version,app_users, andchange_password.
Step 4: Build Service Setup and Compose Artifacts
For create/change/unsuspend actions, the workflow prepares Nginx configuration and a docker-compose file before routing to service actions.
- Open Route Service Setup and keep the OR conditions for
create,change_package, andunsuspendusing{{$('Incoming API Webhook').item.json.body.command}}. - In Prepare Nginx Config, set main to
ignore_invalid_headers off; client_max_body_size 0; proxy_buffering off; proxy_request_buffering off;. - Set main_location to
# Custom header(with the trailing newline). - In Build Compose File, set docker-compose to the full YAML template using expressions like
{{$('Incoming API Webhook').item.json.body.domain}},{{$('Assign Parameters').item.json.mount_dir}},{{$('Incoming API Webhook').item.json.body.ram}}, and{{$('Incoming API Webhook').item.json.body.cpu}}. - Confirm Build Compose File routes to Service Command Router.
Step 5: Configure Service and Container Actions (Grouped)
The workflow uses many set nodes to build shell scripts for different Docker service operations. Configure these nodes consistently and keep their outputs directed to SSH execution.
- Ensure Service Command Router routes to Test Host Connection, Provision Service, Suspend Service, Restore Service, Terminate Service, and Adjust Service Plan.
- Ensure Container Info Router routes to Inspect Container, Collect Container Stats, and Fetch Container Logs.
- Ensure Container Command Router routes to Start Container, Stop Container, Mount Storage, Unmount Storage, Retrieve ACL, Apply ACL, and Retrieve Network Stats.
- Ensure App Command Router routes to Get App Version, List App Users, and Update User Password.
- Verify each of these action nodes sets a sh field with the full bash script and uses expressions like
{{$('Incoming API Webhook').item.json.body.domain}}and{{$('Assign Parameters').item.json.clients_dir}}.
Step 6: Configure SSH Execution and Response Parsing
All command scripts are executed remotely and normalized into a consistent API response.
- Open Remote SSH Runner and set Command to
{{$json.sh}}. - Set Working Directory to
/(expression=/). - Credential Required: Connect your
sshPasswordcredentials. - Verify that every action node connects to Remote SSH Runner, and Remote SSH Runner connects to Parse SSH Result.
- In Parse SSH Result, keep the JavaScript logic that parses
$json.stdoutinto standardizedstatus,message, anddata. - Confirm Parse SSH Result connects to Webhook Response with Respond With set to
allIncomingItems.
Step 7: Test & Activate Your Workflow
Validate execution with test calls and then enable the workflow for production use.
- Click Execute Workflow and send a POST request to the Incoming API Webhook test URL with a valid
server_domainandcommand. - Confirm the execution runs through Validate Domain and then into the correct router branch based on
command. - Verify Remote SSH Runner returns output that Parse SSH Result converts into a
statusofsuccessorerror. - Check the webhook response from Webhook Response to confirm you receive the expected JSON structure.
- Toggle the workflow to Active to enable production use.
Common Gotchas
- Webhook Basic Auth credentials can expire or get changed during security cleanups. If calls start failing, check the n8n webhook node credentials and the WHMCS module config 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 will mostly configure credentials and edit a few parameters. If you want to change the Bash scripts or routing logic, then some scripting comfort helps.
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 server costs for your Docker host (and possibly a VPS if you self-host n8n).
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 it’s one of the best reasons to use this approach. Update the values set in “Assign Parameters,” then tailor “Build Compose File” and “Prepare Nginx Config” so each WHMCS product or plan generates the right ports, volumes, limits, and proxy rules. Many teams also tweak “Adjust Service Plan” to handle upgrades and downgrades cleanly, without rebuilding everything. If you maintain templates in GitHub, you can pull the latest config before provisioning and keep changes tracked.
Most of the time it’s Basic Auth mismatch or the webhook URL changed. Check the “Incoming API Webhook” node credentials, then confirm WHMCS is sending the same username/password and hitting the correct path. If you’re seeing a domain-related failure, the “Validate Domain” node is doing its job, so compare the requested domain to your configured server_domain value. And don’t ignore permissions: if WHMCS can call n8n but SSH can’t run Docker commands, the workflow will still fail at the execution step.
On n8n Cloud Starter, you can handle thousands of executions per month for typical hosting operations, and higher plans handle more. If you self-host, there’s no hard execution cap, but your VPS size and your Docker host performance become the limiting factors. In practice, most actions here run as a single SSH execution, so it scales nicely as long as you avoid huge batch operations during peak hours.
Usually, yes. Zapier and Make are great for SaaS-to-SaaS automations, but this workflow leans on SSH execution, routing, and structured responses, which is more natural in n8n. You also get the option to self-host for unlimited runs, which matters when every suspend, restore, and log fetch counts as an execution. The bigger win is control: you can keep your scripts and security posture exactly how you want, instead of fighting a connector’s limitations. If your setup is very simple and you never need SSH, the other tools can be fine. Talk to an automation expert if you want help choosing.
Once this is running, WHMCS actions line up with what Docker actually did. That alone makes support calmer and provisioning a lot more predictable.
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.