{"id":51,"date":"2026-03-31T07:58:25","date_gmt":"2026-03-31T07:58:25","guid":{"rendered":"https:\/\/kr0311.com\/projects\/?p=51"},"modified":"2026-04-02T13:41:04","modified_gmt":"2026-04-02T13:41:04","slug":"phase-5-custom-hosting-control-panel-build","status":"publish","type":"post","link":"https:\/\/kr0311.com\/projects\/phase-5-custom-hosting-control-panel-build\/","title":{"rendered":"Phase 5: Provisioning Engine &amp; System Integration"},"content":{"rendered":"<p>Up to this point, the custom hosting control panel build has been all about structure.<\/p>\n<p>Phase 4 gave us a solid data layer \u2014 hosting accounts, sites, relationships, and a clean UI to manage them.<\/p>\n<p>This phase builds directly on <a href=\"\/projects\/phase-4-custom-hosting-control-panel-build\/\">Phase 4<\/a>, where the hosting structure and application data model were established properly.<\/p>\n<p>Everything looked right, everything behaved correctly\u2026 but nothing actually touched the system yet.<\/p>\n<p>Phase 5 changes that.<\/p>\n<p>This is the point where the panel stops being a well-organised interface and starts becoming something far more powerful \u2014 a system that can actually control infrastructure.<\/p>\n<p>In short: we move from <strong>data<\/strong> to <strong>action<\/strong>.<\/p>\n<hr \/>\n<h2>\u2699\ufe0f What This Phase Introduces in the Custom Hosting Control Panel Build<\/h2>\n<p>Phase 5 introduces the provisioning layer \u2014 the bridge between the Laravel application and the underlying operating system.<\/p>\n<ul>\n<li>System users are created automatically<\/li>\n<li>Directory structures are built on the server<\/li>\n<li>Commands are executed safely through a controlled layer<\/li>\n<li>The panel begins performing real infrastructure tasks<\/li>\n<\/ul>\n<p>This is the first time the control panel actually <strong>does something outside of itself<\/strong>.<\/p>\n<p>Which is exciting\u2026 and also exactly the point where bad decisions start getting expensive.<\/p>\n<hr \/>\n<h2>\ud83e\udde0 Service Layer Architecture<\/h2>\n<h3>\ud83c\udfd7\ufe0f Why Services Were Introduced<\/h3>\n<p>Running system-level commands directly inside controllers is a very efficient way to build something fragile, messy, and mildly terrifying.<\/p>\n<p>Instead, Phase 5 introduces a proper service layer to keep responsibilities clean and controlled.<\/p>\n<ul>\n<li><strong>ProvisioningService<\/strong> \u2014 handles provisioning logic<\/li>\n<li><strong>SystemCommandService<\/strong> \u2014 handles all system command execution<\/li>\n<\/ul>\n<p>This separation ensures:<\/p>\n<ul>\n<li>Controllers remain clean and predictable<\/li>\n<li>System logic is centralised and testable<\/li>\n<li>Security boundaries are clearly defined<\/li>\n<\/ul>\n<p>The controller no longer <em>does<\/em> provisioning \u2014 it simply <strong>requests it<\/strong>.<\/p>\n<p>Which is a much healthier relationship for everyone involved.<\/p>\n<p>The service-based structure also follows the same general design principles described in the <a href=\"https:\/\/laravel.com\/docs\/container\" target=\"_blank\" rel=\"noopener\">Laravel service container documentation<\/a>, which helps keep responsibilities separated and maintainable as the panel grows.<\/p>\n<hr \/>\n<h2>\ud83d\udd12 Safe Command Execution<\/h2>\n<p>One of the most important decisions in this phase was how commands are executed.<\/p>\n<p>There are plenty of ways to run shell commands in PHP.<\/p>\n<p>Most of them are terrible ideas in a production control panel.<\/p>\n<p>So instead of relying on:<\/p>\n<ul>\n<li><strong>exec()<\/strong><\/li>\n<li><strong>shell_exec()<\/strong><\/li>\n<li>Inline command strings scattered all over the place<\/li>\n<\/ul>\n<p>Everything is routed through a dedicated execution layer.<\/p>\n<p>This gives us:<\/p>\n<ul>\n<li>Centralised command handling<\/li>\n<li>Full output and error capture<\/li>\n<li>Structured logging of every action<\/li>\n<li>A single place to enforce security rules<\/li>\n<\/ul>\n<p>No shortcuts. No surprises. No silent failures disappearing into the void.<\/p>\n<hr \/>\n<h2>\ud83d\udc64 System User Creation<\/h2>\n<p>Each hosting account now maps directly to a Linux system user.<\/p>\n<p>This is a key architectural decision that aligns the application layer with the operating system underneath it.<\/p>\n<ul>\n<li>Username comes directly from the hosting account<\/li>\n<li>A system user is created automatically<\/li>\n<li>No interactive login is allowed<\/li>\n<\/ul>\n<p>This gives us proper ownership, isolation, and a clean foundation for future features.<\/p>\n<p>It also means the panel is no longer just describing accounts in the database \u2014 it is creating something real on the server.<\/p>\n<p>That is a pretty major moment.<\/p>\n<hr \/>\n<h2>\ud83d\udcc1 Directory Structure<\/h2>\n<p>Once the system user is created, the directory structure is built automatically.<\/p>\n<ul>\n<li><strong>\/home\/{username}<\/strong><\/li>\n<li><strong>\/home\/{username}\/public_html<\/strong><\/li>\n<\/ul>\n<p>This mirrors the structure defined back in Phase 4.<\/p>\n<p>The difference now is that it actually exists on the server instead of living as a very confident idea in the database.<\/p>\n<p>Ownership and permissions are applied correctly so that:<\/p>\n<ul>\n<li>The user owns their files<\/li>\n<li>The structure is secure by default<\/li>\n<li>It\u2019s ready for web serving in later phases<\/li>\n<\/ul>\n<p>This is where the earlier convention work starts paying off properly.<\/p>\n<hr \/>\n<h2>\ud83d\udd17 Controller Integration<\/h2>\n<p>The provisioning process is triggered during hosting account creation.<\/p>\n<p>But importantly, the controller does not execute any system logic itself.<\/p>\n<p>The flow is simple and clean:<\/p>\n<ul>\n<li>Create hosting account record<\/li>\n<li>Set status to provisioning<\/li>\n<li>Call <strong>ProvisioningService<\/strong><\/li>\n<li>Update status based on result<\/li>\n<\/ul>\n<p>This keeps the application predictable while still allowing powerful backend actions.<\/p>\n<p>It also avoids the classic trap of turning a controller into a part-time web request handler and part-time operating system manager.<\/p>\n<hr \/>\n<h2>\ud83d\udcca Status System<\/h2>\n<p>Hosting accounts now use meaningful lifecycle states:<\/p>\n<ul>\n<li><strong>provisioning<\/strong><\/li>\n<li><strong>active<\/strong><\/li>\n<li><strong>failed<\/strong><\/li>\n<\/ul>\n<p>This is more than just a label \u2014 it reflects real system state.<\/p>\n<p>The panel now knows whether an account exists in the database <strong>and<\/strong> on the server.<\/p>\n<p>That distinction becomes critical as the system grows, because \u201csaved successfully\u201d and \u201cactually provisioned successfully\u201d are very much not the same thing.<\/p>\n<hr \/>\n<h2>\ud83e\uddfe Logging &amp; Debugging<\/h2>\n<p>A dedicated provisioning log was introduced:<\/p>\n<p><strong>storage\/logs\/provisioning.log<\/strong><\/p>\n<p>Every action is recorded:<\/p>\n<ul>\n<li>Commands executed<\/li>\n<li>Output returned<\/li>\n<li>Errors encountered<\/li>\n<li>Timestamps for each step<\/li>\n<\/ul>\n<p>This gives full visibility into what the system is doing without dumping raw backend chaos into the UI.<\/p>\n<p>When something goes wrong, you do not guess.<\/p>\n<p>You check the log, find the problem, and pretend you definitely expected it.<\/p>\n<hr \/>\n<h2>\ud83d\udee1\ufe0f Error Handling<\/h2>\n<p>Provisioning failures are handled carefully.<\/p>\n<p>If something breaks:<\/p>\n<ul>\n<li>The panel does <strong>not<\/strong> crash<\/li>\n<li>The user does <strong>not<\/strong> see raw system errors<\/li>\n<li>The failure is logged internally<\/li>\n<li>The account is marked as <strong>failed<\/strong><\/li>\n<\/ul>\n<p>This keeps the user experience clean while still giving full control to the developer.<\/p>\n<p>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.<\/p>\n<hr \/>\n<h2>\u26a0\ufe0f Challenges &amp; Key Decisions<\/h2>\n<p>This phase was less about writing code and more about making the right decisions before the code became dangerous.<\/p>\n<ul>\n<li>Avoiding unsafe command execution methods<\/li>\n<li>Designing a controlled system interaction layer<\/li>\n<li>Keeping UI logic completely separate from system logic<\/li>\n<li>Ensuring failures don\u2019t corrupt state or break the panel<\/li>\n<\/ul>\n<p>It is very easy at this stage to \u201cjust make it work\u201d.<\/p>\n<p>The goal here was to make it work <strong>properly<\/strong>.<\/p>\n<p>Because once a control panel starts creating users and directories for real, sloppy design choices stop being theoretical very quickly.<\/p>\n<hr \/>\n<h2>\ud83c\udfa8 UI Refinement \u2013 Moving Towards a Real Panel Experience<\/h2>\n<p>Alongside the backend changes, this phase also continued refining the panel interface.<\/p>\n<p>The default Laravel styling was already replaced in earlier phases, but here the focus shifted toward something more intentional \u2014 a layout that feels closer to a real hosting panel.<\/p>\n<p>Navigation, structure, and interaction patterns were adjusted to better match what users expect from platforms like cPanel.<\/p>\n<ul>\n<li>Clear separation between sections<\/li>\n<li>Consistent navigation behaviour<\/li>\n<li>Structured layouts for accounts and sites<\/li>\n<li>Cleaner visual hierarchy across the panel<\/li>\n<\/ul>\n<p>No drastic redesign \u2014 just deliberate evolution.<\/p>\n<p>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.<\/p>\n<hr \/>\n<h2>\ud83d\udd25 Why This Phase Matters<\/h2>\n<p>This is the moment the project changes.<\/p>\n<p>Before Phase 5, the panel managed data.<\/p>\n<p>After Phase 5, the panel manages the server.<\/p>\n<p>That is a big shift.<\/p>\n<p>We now have:<\/p>\n<ul>\n<li>A safe execution layer<\/li>\n<li>Real system interaction<\/li>\n<li>A foundation for full automation<\/li>\n<\/ul>\n<p>This is where the control panel starts to earn its name.<\/p>\n<hr \/>\n<h2>\ud83d\udd1c What\u2019s Next \u2013 Phase 6<\/h2>\n<p>With system users and directories in place, the next step is bringing the web stack into play.<\/p>\n<ul>\n<li>Nginx vhost generation<\/li>\n<li>PHP-FPM pool creation<\/li>\n<li>Domain binding<\/li>\n<li>Safe service reloads<\/li>\n<\/ul>\n<p>This is where sites become accessible \u2014 not just created.<\/p>\n<p>Which, naturally, is where things get even more interesting.<\/p>\n<hr \/>\n<h2>\ud83d\udcad Final Thoughts<\/h2>\n<p>This phase is where things get serious.<\/p>\n<p>The groundwork from earlier phases really starts to pay off \u2014 because without that structure, this would have turned into chaos very quickly.<\/p>\n<p>Instead, everything slots into place cleanly.<\/p>\n<p>The panel now has control, but more importantly, it has <strong>controlled control<\/strong>.<\/p>\n<p>And that is exactly how it should be.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Phase 5 of the custom hosting control panel build introduces the provisioning engine, enabling system user creation, directory setup, and safe server-side automation.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[34,20,50,51,48,47,38,49,52,46],"class_list":["post-51","post","type-post","status-publish","format-standard","hentry","category-control-panel","tag-control-panel-build","tag-custom-hosting-control-panel","tag-infrastructure-automation","tag-laravel-services","tag-linux-user-management","tag-provisioning-engine","tag-self-hosted-control-panel","tag-server-automation","tag-server-provisioning","tag-system-integration"],"_links":{"self":[{"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/posts\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":4,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":107,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/posts\/51\/revisions\/107"}],"wp:attachment":[{"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kr0311.com\/projects\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}