Up to this point, the custom hosting control panel build has been all about structure.
Phase 4 gave us a solid data layer — hosting accounts, sites, relationships, and a clean UI to manage them.
This phase builds directly on Phase 4, where the hosting structure and application data model were established properly.
Everything looked right, everything behaved correctly… but nothing actually touched the system yet.
Phase 5 changes that.
This is the point where the panel stops being a well-organised interface and starts becoming something far more powerful — a system that can actually control infrastructure.
In short: we move from data to action.
⚙️ What This Phase Introduces in the Custom Hosting Control Panel Build
Phase 5 introduces the provisioning layer — the bridge between the Laravel application and the underlying operating system.
- System users are created automatically
- Directory structures are built on the server
- Commands are executed safely through a controlled layer
- The panel begins performing real infrastructure tasks
This is the first time the control panel actually does something outside of itself.
Which is exciting… and also exactly the point where bad decisions start getting expensive.
🧠 Service Layer Architecture
🏗️ Why Services Were Introduced
Running system-level commands directly inside controllers is a very efficient way to build something fragile, messy, and mildly terrifying.
Instead, Phase 5 introduces a proper service layer to keep responsibilities clean and controlled.
- ProvisioningService — handles provisioning logic
- SystemCommandService — handles all system command execution
This separation ensures:
- Controllers remain clean and predictable
- System logic is centralised and testable
- Security boundaries are clearly defined
The controller no longer does provisioning — it simply requests it.
Which is a much healthier relationship for everyone involved.
The service-based structure also follows the same general design principles described in the Laravel service container documentation, which helps keep responsibilities separated and maintainable as the panel grows.
🔒 Safe Command Execution
One of the most important decisions in this phase was how commands are executed.
There are plenty of ways to run shell commands in PHP.
Most of them are terrible ideas in a production control panel.
So instead of relying on:
- exec()
- shell_exec()
- Inline command strings scattered all over the place
Everything is routed through a dedicated execution layer.
This gives us:
- Centralised command handling
- Full output and error capture
- Structured logging of every action
- A single place to enforce security rules
No shortcuts. No surprises. No silent failures disappearing into the void.
👤 System User Creation
Each hosting account now maps directly to a Linux system user.
This is a key architectural decision that aligns the application layer with the operating system underneath it.
- Username comes directly from the hosting account
- A system user is created automatically
- No interactive login is allowed
This gives us proper ownership, isolation, and a clean foundation for future features.
It also means the panel is no longer just describing accounts in the database — it is creating something real on the server.
That is a pretty major moment.
📁 Directory Structure
Once the system user is created, the directory structure is built automatically.
- /home/{username}
- /home/{username}/public_html
This mirrors the structure defined back in Phase 4.
The difference now is that it actually exists on the server instead of living as a very confident idea in the database.
Ownership and permissions are applied correctly so that:
- The user owns their files
- The structure is secure by default
- It’s ready for web serving in later phases
This is where the earlier convention work starts paying off properly.
🔗 Controller Integration
The provisioning process is triggered during hosting account creation.
But importantly, the controller does not execute any system logic itself.
The flow is simple and clean:
- Create hosting account record
- Set status to provisioning
- Call ProvisioningService
- Update status based on result
This keeps the application predictable while still allowing powerful backend actions.
It also avoids the classic trap of turning a controller into a part-time web request handler and part-time operating system manager.
📊 Status System
Hosting accounts now use meaningful lifecycle states:
- provisioning
- active
- failed
This is more than just a label — it reflects real system state.
The panel now knows whether an account exists in the database and on the server.
That distinction becomes critical as the system grows, because “saved successfully” and “actually provisioned successfully” are very much not the same thing.
🧾 Logging & Debugging
A dedicated provisioning log was introduced:
storage/logs/provisioning.log
Every action is recorded:
- Commands executed
- Output returned
- Errors encountered
- Timestamps for each step
This gives full visibility into what the system is doing without dumping raw backend chaos into the UI.
When something goes wrong, you do not guess.
You check the log, find the problem, and pretend you definitely expected it.
🛡️ Error Handling
Provisioning failures are handled carefully.
If something breaks:
- The panel does not crash
- The user does not see raw system errors
- The failure is logged internally
- The account is marked as failed
This keeps the user experience clean while still giving full control to the developer.
Which is exactly what you want when the thing failing is a real server-side action and not just a missing div on a page somewhere.
⚠️ Challenges & Key Decisions
This phase was less about writing code and more about making the right decisions before the code became dangerous.
- Avoiding unsafe command execution methods
- Designing a controlled system interaction layer
- Keeping UI logic completely separate from system logic
- Ensuring failures don’t corrupt state or break the panel
It is very easy at this stage to “just make it work”.
The goal here was to make it work properly.
Because once a control panel starts creating users and directories for real, sloppy design choices stop being theoretical very quickly.
🎨 UI Refinement – Moving Towards a Real Panel Experience
Alongside the backend changes, this phase also continued refining the panel interface.
The default Laravel styling was already replaced in earlier phases, but here the focus shifted toward something more intentional — a layout that feels closer to a real hosting panel.
Navigation, structure, and interaction patterns were adjusted to better match what users expect from platforms like cPanel.
- Clear separation between sections
- Consistent navigation behaviour
- Structured layouts for accounts and sites
- Cleaner visual hierarchy across the panel
No drastic redesign — just deliberate evolution.
The goal is simple: when someone uses the panel, it should feel familiar, not experimental, confused, or like it was built during a caffeine emergency.
🔥 Why This Phase Matters
This is the moment the project changes.
Before Phase 5, the panel managed data.
After Phase 5, the panel manages the server.
That is a big shift.
We now have:
- A safe execution layer
- Real system interaction
- A foundation for full automation
This is where the control panel starts to earn its name.
🔜 What’s Next – Phase 6
With system users and directories in place, the next step is bringing the web stack into play.
- Nginx vhost generation
- PHP-FPM pool creation
- Domain binding
- Safe service reloads
This is where sites become accessible — not just created.
Which, naturally, is where things get even more interesting.
💭 Final Thoughts
This phase is where things get serious.
The groundwork from earlier phases really starts to pay off — because without that structure, this would have turned into chaos very quickly.
Instead, everything slots into place cleanly.
The panel now has control, but more importantly, it has controlled control.
And that is exactly how it should be.