[{"data":1,"prerenderedAt":1878},["ShallowReactive",2],{"productsSearch":3,"openSourceSearch":132,"postsSearch":270,"guidesSearch":1062,"blog-designing-an-installation-script":1106,"guides-post-\u002Fguides\u002Fdesigning-your-saas-app-to-be-self-hostable\u002F":1216,"guides-posts-\u002Fguides\u002Fdesigning-your-saas-app-to-be-self-hostable\u002F":1303,"header-products":1674,"footer-products":1721,"header-open-source":1768,"footer-open-source":1823},[4,35,56,79,107],{"id":5,"title":6,"badge":7,"badges":8,"buy_url":7,"complete_package_url":7,"description":15,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":18,"logo_wide":19,"meta":20,"screenshot":28,"short_description":29,"short_title":7,"stem":30,"trial_url":31,"type":32,"url":33,"__hash__":34},"products\u002Fproducts\u002F1.selfhost-pro.md","Self-Host Pro",null,[9,12],{"label":10,"color":11},"Docker","#2970FF",{"label":13,"color":14},"Product Feedback","#EF6820","Sell self-hosted software in minutes. Push your Docker image, connect Stripe, and let your customers install with a single command.","md",true,"\u002Flogos\u002Fselfhostpro-white-icon.svg","\u002Flogos\u002Fselfhostpro.svg",{"body":21},{"type":22,"value":23,"toc":24},"minimark",[],{"title":25,"searchDepth":26,"depth":26,"links":27},"",2,[],"\u002Fscreenshots\u002Fselfhostpro.png","Sell self-hosted software in minutes.","products\u002F1.selfhost-pro","https:\u002F\u002Fapp.selfhostpro.com\u002Fregister","saas","https:\u002F\u002Fselfhostpro.com","gypXiPoYhs1B7AY78sbauHLg211rppYZ09jAPGvFsUc",{"id":36,"title":37,"badge":7,"badges":38,"buy_url":7,"complete_package_url":7,"description":42,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":43,"logo_wide":44,"meta":45,"screenshot":50,"short_description":51,"short_title":7,"stem":52,"trial_url":53,"type":32,"url":54,"__hash__":55},"products\u002Fproducts\u002F2.bugflow.md","Bugflow",[39,41],{"label":40,"color":11},"Self-hosted",{"label":13,"color":14},"Improve your product through visual product feedback. Bugflow captures feedback from customers and internal testers and puts them into your existing project management flow.","\u002Flogos\u002Fbugflow-icon.svg","\u002Flogos\u002Fbugflow.svg",{"body":46},{"type":22,"value":47,"toc":48},[],{"title":25,"searchDepth":26,"depth":26,"links":49},[],"\u002Fscreenshots\u002Fbugflow.png","Get product feedback directly in GitHub, GitLab, and more.","products\u002F2.bugflow","https:\u002F\u002Fapp.bugflow.io\u002Fregister","https:\u002F\u002Fbugflow.io","Y_xd9HKE2TRfS8ESktytwfmJpXu0e059YwSKRkwv2CY",{"id":57,"title":58,"badge":59,"badges":60,"buy_url":65,"complete_package_url":7,"description":66,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":67,"logo_wide":68,"meta":69,"screenshot":74,"short_description":75,"short_title":7,"stem":76,"trial_url":7,"type":32,"url":77,"__hash__":78},"products\u002Fproducts\u002F3.spin-pro.md","Spin Pro","New",[61,62],{"label":40,"color":11},{"label":63,"color":64},"Starter Kit","#EE46BC","https:\u002F\u002Fstore.serversideup.net\u002Fbuy\u002F9cf51bbc-2a86-4ea1-958b-c4d69e05cde6?checkout%5Bdiscount_code%5D=FIRST50","Based on a collection of our open source projects, Spin Pro enables Laravel Pros to ship software with the power of a PaaS to any VPS host of their choice. Available as PAY ONCE infrastructure.","\u002Flogos\u002Fspin-icon.svg","\u002Flogos\u002Fspin-pro.svg",{"body":70},{"type":22,"value":71,"toc":72},[],{"title":25,"searchDepth":26,"depth":26,"links":73},[],"\u002Fscreenshots\u002Fspin-pro.png","Production-ready Docker templates for shipping quickly.","products\u002F3.spin-pro","https:\u002F\u002Fgetspin.pro","QXhl5EbmucVt6wLNUnGN9Hr5kCb-qrrTq3TnfnCa6Ik",{"id":80,"title":81,"badge":7,"badges":82,"buy_url":89,"complete_package_url":89,"description":90,"essentials_url":91,"extension":16,"external":92,"logo_component":93,"logo_square":94,"logo_wide":7,"meta":95,"screenshot":100,"short_description":101,"short_title":102,"stem":103,"trial_url":7,"type":104,"url":105,"__hash__":106},"products\u002Fproducts\u002F4.browser-extensions-book.md","Building Browser Extensions",[83,86],{"label":84,"color":85},"Book","#17B26A",{"label":87,"color":88},"Source Code","#0BA5EC","https:\u002F\u002Fstore.serversideup.net\u002Fcheckout\u002Fbuy\u002F5ec75643-2fc5-4366-ae67-213a217c4e54?checkout","Learn how to efficiently build browser extensions for every major browser from the same codebase. Source code examples for every major JavaScript framework is included.","https:\u002F\u002Fstore.serversideup.net\u002Fcheckout\u002Fbuy\u002F96e2d787-e797-4e13-9c8d-0aeb7b687844",false,"LogosChrome","\u002Flogos\u002Fchrome.svg",{"body":96},{"type":22,"value":97,"toc":98},[],{"title":25,"searchDepth":26,"depth":26,"links":99},[],"\u002Fscreenshots\u002Fbrowser-extensions-book.png","Build browser extensions for Firefox, Chrome, and more.","Building Browser Ext.","products\u002F4.browser-extensions-book","book","\u002Fproducts\u002Fbuilding-multi-platform-browser-extensions\u002F","s5qFx4_mAvZQ1oY9mxqJtMDG-IUpZ4viKXGB2cpCqUE",{"id":108,"title":109,"badge":7,"badges":110,"buy_url":116,"complete_package_url":116,"description":117,"essentials_url":118,"extension":16,"external":92,"logo_component":119,"logo_square":120,"logo_wide":7,"meta":121,"screenshot":126,"short_description":127,"short_title":128,"stem":129,"trial_url":7,"type":104,"url":130,"__hash__":131},"products\u002Fproducts\u002F5.api-spa-book.md","Ultimate Guide To Building APIs & SPAs",[111,112,113],{"label":84,"color":85},{"label":87,"color":88},{"label":114,"color":115},"Videos","#7A5AF8","https:\u002F\u002Fserversideup.lemonsqueezy.com\u002Fcheckout\u002Fbuy\u002F10d32965-4348-4502-a8cc-cf4895c4a841?checkout%5Bdiscount_code%5D=LAUNCHCOMPLETE","Learn how you can use a Laravel API with a Nuxt frontend. You'll then learn how you can take that same Nuxt code and compile it into iOS and Android apps using the same codebase with Capacitor. Includes source code, Figma templates, videos, and more.","https:\u002F\u002Fserversideup.lemonsqueezy.com\u002Fcheckout\u002Fbuy\u002F69a4e541-e74b-4c40-9cde-15fd9a13c6da?checkout%5Bdiscount_code%5D=LAUNCHESSENTIALS","LogosAppStore","\u002Flogos\u002Fapp-store-icon.svg",{"body":122},{"type":22,"value":123,"toc":124},[],{"title":25,"searchDepth":26,"depth":26,"links":125},[],"\u002Fscreenshots\u002Fapi-book.png","Build web and mobile apps from the same codebase.","Guide to APIs & SPAs","products\u002F5.api-spa-book","\u002Fproducts\u002Fultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3\u002F","D4l8dwFfsCcIeSTW_mGwnj9ntvx-ndSDzDga4syx2R8",[133,160,182,205,229,251],{"id":134,"title":135,"badge":7,"badges":136,"description":143,"extension":16,"external":92,"github_slug":144,"logo_square":145,"logo_wide":146,"meta":147,"repo_url":152,"screenshot":153,"short_description":154,"short_title":155,"stem":156,"type":157,"url":158,"__hash__":159},"openSource\u002Fopen-source\u002F1.docker-php.md","Docker PHP",[137,140],{"label":138,"color":139},"Open Source","#85888E",{"label":141,"color":142},"Docker Image","#2E90FA","Our open source PHP Docker images are production-ready and optimized for Laravel and WordPress. Configuration is a breeze with environment variables. Choose between FPM+NGINX, NGINX Unit, and more.","serversideup\u002Fdocker-php","\u002Flogos\u002Fdocker-php-icon.svg","\u002Flogos\u002Fdocker-php.svg",{"body":148},{"type":22,"value":149,"toc":150},[],{"title":25,"searchDepth":26,"depth":26,"links":151},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Fdocker-php","\u002Fscreenshots\u002Fdocker-php.png","Supercharged PHP Docker images, based off the official PHP images.","serversideup\u002Fphp","open-source\u002F1.docker-php","docker-image","https:\u002F\u002Fserversideup.net\u002Fopen-source\u002Fdocker-php\u002F","n3MDRhVVgGQLn9OfH6Hh1RXTN2LO0npzihr14Ju_Q8Y",{"id":161,"title":162,"badge":7,"badges":163,"description":167,"extension":16,"external":92,"github_slug":168,"logo_square":67,"logo_wide":169,"meta":170,"repo_url":175,"screenshot":176,"short_description":177,"short_title":7,"stem":178,"type":179,"url":180,"__hash__":181},"openSource\u002Fopen-source\u002F2.spin.md","Spin",[164,165],{"label":138,"color":139},{"label":166,"color":14},"Deployment Tool","Get the power of running a PaaS on any VPS host of your choice. Run your application in a 100% replicated workflow on macOS, Windows, and Linux all using Docker.","serversideup\u002Fspin","\u002Flogos\u002Fspin.svg",{"body":171},{"type":22,"value":172,"toc":173},[],{"title":25,"searchDepth":26,"depth":26,"links":174},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Fspin","\u002Fscreenshots\u002Fspin.png","Docker Simplified. Deploy Anywhere. Zero Downtime. Any OS.","open-source\u002F2.spin","tool","https:\u002F\u002Fserversideup.net\u002Fopen-source\u002Fspin\u002F","3wkEWcMFZ07pUCDQR2PS0I3BE0GhqZWUUWEl9Ne2hks",{"id":183,"title":184,"badge":7,"badges":185,"description":189,"extension":16,"external":92,"github_slug":190,"logo_square":191,"logo_wide":192,"meta":193,"repo_url":198,"screenshot":199,"short_description":200,"short_title":7,"stem":201,"type":202,"url":203,"__hash__":204},"openSource\u002Fopen-source\u002F3.financial-freedom.md","Financial Freedom",[186,187],{"label":138,"color":139},{"label":188,"color":85},"Budgeting Tool","An open source alternative to Mint and YNAB. Track your expenses and never worry about your financial data being used by investors again.","serversideup\u002Ffinancial-freedom","\u002Flogos\u002Ffinancial-freedom-white-icon.svg","\u002Flogos\u002Ffinancial-freedom.svg",{"body":194},{"type":22,"value":195,"toc":196},[],{"title":25,"searchDepth":26,"depth":26,"links":197},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Ffinancial-freedom","\u002Fscreenshots\u002Ffinancial-freedom.png","Open source alternative to Mint, YNAB, and more.","open-source\u002F3.financial-freedom","app","https:\u002F\u002Fserversideup.net\u002Fopen-source\u002Ffinancial-freedom\u002F","Jg0Q8582POghRvbb20PbbEAhD1TE9wqX0eaazoaNmXI",{"id":206,"title":207,"badge":7,"badges":208,"description":213,"extension":16,"external":92,"github_slug":214,"logo_square":215,"logo_wide":216,"meta":217,"repo_url":222,"screenshot":223,"short_description":224,"short_title":7,"stem":225,"type":226,"url":227,"__hash__":228},"openSource\u002Fopen-source\u002F4.amplitudejs.md","AmplitudeJS",[209,210],{"label":138,"color":139},{"label":211,"color":212},"HTML5 Audio Library","#6172F3","Gain full control of the web audio element in HTML5. Design your audio player to exactly match your vision. Use visualizers to take your audio to the next level.","521dimensions\u002Famplitudejs","\u002Flogos\u002Famplitude-white-icon.svg","\u002Flogos\u002Famplitudejs.svg",{"body":218},{"type":22,"value":219,"toc":220},[],{"title":25,"searchDepth":26,"depth":26,"links":221},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Famplitudejs","\u002Fscreenshots\u002Famplitude.png","Customize the design of any element of the HTML5 Audio Player.","open-source\u002F4.amplitudejs","library","https:\u002F\u002Fserversideup.net\u002Fopen-source\u002Famplitudejs\u002F","R3gWpQhxl9MpRlgAmUDocJIiXI4GkM2rlVh4zNa4vC8",{"id":230,"title":231,"badge":7,"badges":232,"description":236,"extension":16,"external":92,"github_slug":237,"logo_square":238,"logo_wide":239,"meta":240,"repo_url":245,"screenshot":246,"short_description":247,"short_title":7,"stem":248,"type":226,"url":249,"__hash__":250},"openSource\u002Fopen-source\u002F5.webext-bridge.md","webext-bridge",[233,234],{"label":138,"color":139},{"label":235,"color":14},"Browser Extensions","Simplify the communication between your browser extension's components with a type-safe messaging library for Chrome, Firefox, and Edge.","serversideup\u002Fwebext-bridge","\u002Flogos\u002Fweb-ext-bridge-icon.svg","\u002Flogos\u002Fwebext-bridge.svg",{"body":241},{"type":22,"value":242,"toc":243},[],{"title":25,"searchDepth":26,"depth":26,"links":244},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Fwebext-bridge","\u002Fscreenshots\u002Fwebext-bridge.png","Messaging in Web Extensions made easy. Batteries included.","open-source\u002F5.webext-bridge","https:\u002F\u002Fserversideup.net\u002Fopen-source\u002Fwebext-bridge\u002F","vo7OidHO3fjG5C819Z2hkTIj_IG0QiB_Ubqsi7bWLM8",{"id":252,"title":253,"badge":7,"badges":254,"description":257,"extension":16,"external":17,"github_slug":258,"logo_square":259,"logo_wide":7,"meta":260,"repo_url":265,"screenshot":7,"short_description":266,"short_title":267,"stem":268,"type":157,"url":265,"__hash__":269},"openSource\u002Fopen-source\u002F6.docker-ansible.md","Docker Ansible",[255,256],{"label":138,"color":139},{"label":141,"color":142},"Run Ansible anywhere with a lightweight and powerful Docker image for automation and configuration management.","serversideup\u002Fdocker-ansible","\u002Flogos\u002Fansible-icon.svg",{"body":261},{"type":22,"value":262,"toc":263},[],{"title":25,"searchDepth":26,"depth":26,"links":264},[],"https:\u002F\u002Fgithub.com\u002Fserversideup\u002Fdocker-ansible","Run Ansible anywhere with a lightweight and powerful Docker image.","serversideup\u002Fansible","open-source\u002F6.docker-ansible","77f1RaRu4xdWkmjM6pVXwQqXxN0nlTEu6zFcMthppzQ",[271,278,284,289,295,302,307,312,317,323,328,333,338,343,348,353,358,363,369,374,379,384,389,394,399,404,409,415,420,425,430,434,439,444,449,454,459,463,467,471,476,481,485,490,495,500,505,510,515,520,526,531,535,541,546,550,556,561,566,571,576,580,585,590,595,600,606,611,616,621,626,631,636,641,646,651,656,661,666,671,675,680,685,690,695,700,705,709,713,718,723,728,732,737,742,747,752,756,760,764,769,773,778,783,788,793,798,803,807,812,816,821,826,831,836,841,846,851,856,861,866,870,875,880,885,890,895,900,905,910,915,920,925,929,934,939,944,949,954,958,962,966,971,975,980,984,988,992,996,1001,1006,1011,1015,1019,1024,1029,1034,1039,1043,1048,1053,1058],{"path":272,"title":273,"categories":274,"date":277},"\u002Fblog\u002Faccessing-route-parameters-in-nuxt-3","Accessing Route Parameters in Nuxt 3",[275,276],"nuxt","vue","2022-03-30",{"path":279,"title":280,"categories":281,"date":283},"\u002Fblog\u002Fadd-api-end-points-laravel","Add Laravel API Endpoints",[282],"laravel","2017-10-09",{"path":285,"title":286,"categories":287,"date":288},"\u002Fblog\u002Fadding-laravel-user-profiles","Adding Laravel User Profiles",[282],"2018-01-11",{"path":290,"title":291,"categories":292,"date":294},"\u002Fblog\u002Fadding-server-configurations-within-your-laravel-app","Adding server configurations within your Laravel App",[282,293],"devops","2023-05-24",{"path":296,"title":297,"categories":298,"date":301},"\u002Fblog\u002Fadopt-a-ubiquiti-usg-to-a-unifi-cloud-controller-automate-device-deployments","Adopt a Ubiquiti USG to a UniFi Cloud Controller & automate device deployments",[299,300],"unifi","networking","2018-06-20",{"path":303,"title":304,"categories":305,"date":306},"\u002Fblog\u002Fadvanced-data-fetching-with-nuxt-3","Advanced Data Fetching with Nuxt 3",[275],"2023-01-26",{"path":308,"title":309,"categories":310,"date":311},"\u002Fblog\u002Fadvanced-meilisearch-queries-with-laravel-scout","Advanced Meilisearch Queries with Laravel Scout",[282],"2022-04-18",{"path":313,"title":314,"categories":315,"date":316},"\u002Fblog\u002Fadvanced-vuex-4-tips","Advanced Vuex 4 Tips",[276],"2021-09-28",{"path":318,"title":319,"categories":320,"date":322},"\u002Fblog\u002Famplitudejs-configuration-options","AmplitudeJS Configuration Options",[321],"javascript","2018-09-20",{"path":324,"title":325,"categories":326,"date":327},"\u002Fblog\u002Famplitudejs-live-stream-html5-audio","AmplitudeJS for Live Stream HTML5 Audio",[321],"2014-04-05",{"path":329,"title":330,"categories":331,"date":332},"\u002Fblog\u002Fanimista-css-with-vuejs-transitions","Animista CSS Animations with VueJS Transitions",[276],"2018-05-17",{"path":334,"title":335,"categories":336,"date":337},"\u002Fblog\u002Fapi-form-submissions-javascript-vuex-laravel","API Driven Form Submissions with Javascript, Vuex and Laravel",[282,276],"2017-10-26",{"path":339,"title":340,"categories":341,"date":342},"\u002Fblog\u002Fappend-gravatar-attribute-to-the-laravel-eloquent-user-model","Append Gravatar Attribute to the Laravel Eloquent User Model",[282],"2021-12-07",{"path":344,"title":345,"categories":346,"date":347},"\u002Fblog\u002Fautomatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers","Automatic Controller Assignment for UniFi DHCP Option 43 on Mikrotik Routers",[299,300],"2017-09-13",{"path":349,"title":350,"categories":351,"date":352},"\u002Fblog\u002Fbasic-get-requests-with-fetch-api-and-vuejs","Basic GET Requests with Fetch API and VueJS",[276],"2020-11-18",{"path":354,"title":355,"categories":356,"date":357},"\u002Fblog\u002Fbeginning-vuex-4-with-vue-3","Beginning Vuex 4 with Vue 3",[276],"2021-09-07",{"path":359,"title":360,"categories":361,"date":362},"\u002Fblog\u002Fbest-practices-for-planning-for-unifi-video","Best practices for planning for UniFi Video",[299,300],"2018-06-22",{"path":364,"title":365,"categories":366,"date":368},"\u002Fblog\u002Fbrowser-extension-messaging","Browser Extension Messaging",[367],"browserextensions","2023-12-12",{"path":370,"title":371,"categories":372,"date":373},"\u002Fblog\u002Fbuild-an-api-wrapper-with-vuejs-axios","Build an API Wrapper with VueJS & Axios",[276],"2020-11-16",{"path":375,"title":376,"categories":377,"date":378},"\u002Fblog\u002Fbuild-api-requests-javascript","Build Out API Requests in Javascript",[321],"2017-10-12",{"path":380,"title":381,"categories":382,"date":383},"\u002Fblog\u002Fbuild-vuex-module","Build a Vuex Module",[276],"2017-10-16",{"path":385,"title":386,"categories":387,"date":388},"\u002Fblog\u002Fbuilding-a-queue-with-vue-3-and-vuex-4","Building a Queue with Vue 3 and Vuex 4",[276],"2022-03-17",{"path":390,"title":391,"categories":392,"date":393},"\u002Fblog\u002Fbuilding-a-single-song-player","Building a Single Song Player",[321],"2018-08-30",{"path":395,"title":396,"categories":397,"date":398},"\u002Fblog\u002Fbuilding-page-layout-vue-router","Building a Page Layout for Vue Router",[282,276],"2017-10-23",{"path":400,"title":401,"categories":402,"date":403},"\u002Fblog\u002Fcaching-api-endpoints-with-laravel","Caching API Endpoints with Laravel",[282],"2022-09-27",{"path":405,"title":406,"categories":407,"date":408},"\u002Fblog\u002Fcapturing-an-image-from-an-html5-canvas-or-video-element","Capturing an image from an HTML5 Canvas or Video Element",[321],"2021-10-05",{"path":410,"title":411,"categories":412,"date":414},"\u002Fblog\u002Fchoosing-your-services-and-database","Choosing Your Services and Database",[413],"programming","2026-03-26",{"path":416,"title":417,"categories":418,"date":419},"\u002Fblog\u002Fcisco-ios-command-cheat-sheet-for-routers-and-switches","Cisco IOS Command Cheat Sheet for Routers and Switches",[293],"2011-11-22",{"path":421,"title":422,"categories":423,"date":424},"\u002Fblog\u002Fclean-vuejs-public-private-routes","VueJS Route Security and Authentication",[276],"2018-01-04",{"path":426,"title":427,"categories":428,"date":429},"\u002Fblog\u002Fcollections-blueprints-and-entries-with-statamic-3","Collections, Blueprints, and Entries with Statamic 3",[282],"2022-03-12",{"path":431,"title":432,"categories":433,"date":362},"\u002Fblog\u002Fconfigure-a-secure-guest-wireless-network-using-vlans-firewalls-and-throttling","Configure a secure guest wireless network using VLANs, firewalls, and throttling",[299,300],{"path":435,"title":436,"categories":437,"date":438},"\u002Fblog\u002Fconfigure-stripe-to-work-with-laravel-cashier-in-laravel-6","Configure Stripe to Work with Laravel Cashier in Laravel 6",[282],"2019-12-17",{"path":440,"title":441,"categories":442,"date":443},"\u002Fblog\u002Fconfiguring-axios-globally-with-vuejs","Configuring Axios Globally with VueJS",[276],"2020-08-03",{"path":445,"title":446,"categories":447,"date":448},"\u002Fblog\u002Fconfiguring-js-sass-single-page-app","Configuring JS and SASS for a Single Page App",[276,282],"2017-09-28",{"path":450,"title":451,"categories":452,"date":453},"\u002Fblog\u002Fconfiguring-vue-router-single-page-app","Configuring Vue Router for a Single Page App",[276],"2019-08-10",{"path":455,"title":456,"categories":457,"date":458},"\u002Fblog\u002Fcordova-app-icon-sizes","Creating Cordova App Icon Sizes the Fast Way",[321],"2017-08-31",{"path":460,"title":461,"categories":462,"date":438},"\u002Fblog\u002Fcreating-a-stripe-subscription-with-laravel-cashier-laravel-passport","Creating a Stripe Subscription with Laravel Cashier + Laravel Passport",[282,276],{"path":464,"title":465,"categories":466,"date":438},"\u002Fblog\u002Fcreating-saas-products-in-stripe-to-sell-with-laravel-cashier","Creating SAAS Products in Stripe to Sell with Laravel Cashier",[282],{"path":468,"title":469,"categories":470,"date":438},"\u002Fblog\u002Fcreating-stripe-setup-intents-with-laravel-api-and-vuejs-spa","Creating Stripe Setup Intents With Laravel API and VueJS SPA",[276],{"path":472,"title":473,"categories":474,"date":475},"\u002Fblog\u002Fcustom-component-v-model-attribute-with-vue-3","Custom Component v-model attribute with Vue 3",[276],"2022-01-27",{"path":477,"title":478,"categories":479,"date":480},"\u002Fblog\u002Fcustom-google-maps-info-windows","Custom Google Maps Info Windows",[276],"2017-11-09",{"path":482,"title":483,"categories":484,"date":480},"\u002Fblog\u002Fcustom-markers-google-map","Custom Markers on Google Map",[276],{"path":486,"title":487,"categories":488,"date":489},"\u002Fblog\u002Fcustomize-google-map-info-windows","Customize Google Map Info Windows",[276],"2017-12-14",{"path":491,"title":492,"categories":493,"date":494},"\u002Fblog\u002Fcustomize-html-audio-css-amplitudejs","Customize HTML Audio via CSS -- Introducing AmplitudeJS",[321],"2014-04-04",{"path":496,"title":497,"categories":498,"date":499},"\u002Fblog\u002Fcustomizing-and-displaying-tags-in-statamic-3","Customizing and Displaying Tags in Statamic 3",[282],"2022-03-15",{"path":501,"title":502,"categories":503,"date":504},"\u002Fblog\u002Fdeploy-unifi-cloud-controller-5-10-minutes","Deploy a UniFi Cloud Controller for $5 and under 10 minutes",[299,300],"2017-11-30",{"path":506,"title":507,"categories":508,"date":509},"\u002Fblog\u002Fdesigning-an-installation-script","Designing an Installation Script",[413],"2026-04-08",{"path":511,"title":512,"categories":513,"date":514},"\u002Fblog\u002Fdetect-if-click-is-inside-an-element-with-javascript","Detect if Click is Inside an Element with JavaScript",[321],"2022-10-11",{"path":516,"title":517,"categories":518,"date":519},"\u002Fblog\u002Fdisplaying-resources-google-map-vue-js","Displaying Resources on a Google Map With Vue JS",[276],"2017-11-06",{"path":521,"title":522,"categories":523,"date":525},"\u002Fblog\u002Fdocker-compose-bind-mounts-vs-named-volumes","Docker Compose Bind Mounts vs Named Volumes: When to Use Each",[524,293],"docker","2026-04-02",{"path":527,"title":528,"categories":529,"date":530},"\u002Fblog\u002Fdrag-and-drop-file-uploads-with-vuejs-and-axios","Drag and Drop File Uploads with VueJS and Axios",[276],"2018-01-09",{"path":532,"title":533,"categories":534,"date":311},"\u002Fblog\u002Fdynamic-api-requests-with-nuxt-3","Dynamic API Requests with Nuxt 3",[275],{"path":536,"title":537,"categories":538,"date":540},"\u002Fblog\u002Fdynamic-autocomplete-options-with-ajax-javascript-jquery-and-php","Dynamic Autocomplete Options with AJAX, Javascript, JQuery and PHP",[321,539],"php","2012-10-08",{"path":542,"title":543,"categories":544,"date":545},"\u002Fblog\u002Fdynamic-forms-vuejs","Dynamic Forms with VueJS",[276],"2017-11-16",{"path":547,"title":548,"categories":549,"date":545},"\u002Fblog\u002Feloquent-parent-child-relationship-laravel","Eloquent Parent Child Relationship in Laravel",[282],{"path":551,"title":552,"categories":553,"date":555},"\u002Fblog\u002Fenable-key-based-ssh-authentication-on-synology-servers","Enable key-based SSH authentication on Synology servers",[554],"synology","2018-08-28",{"path":557,"title":558,"categories":559,"date":560},"\u002Fblog\u002Fevent-handler-tips-for-javascript-and-vuejs","Event Handler Tips for JavaScript and VueJS",[276],"2022-09-13",{"path":562,"title":563,"categories":564,"date":565},"\u002Fblog\u002Ffavoriting-liking-laravel-vuejs","Favoriting or Liking With Laravel and VueJS",[276,282],"2017-11-20",{"path":567,"title":568,"categories":569,"date":570},"\u002Fblog\u002Ffetch-api-components-with-vue-3-composition-api","Fetch API Components with Vue 3 Composition API",[276],"2021-11-30",{"path":572,"title":573,"categories":574,"date":575},"\u002Fblog\u002Ffile-management-with-vuejs-and-laravel","File Management with VueJS and Laravel",[276,282],"2018-01-15",{"path":577,"title":578,"categories":579,"date":530},"\u002Fblog\u002Ffile-upload-progress-indicator-with-axios-and-vuejs","File Upload Progress Indicator with Axios and VueJS",[276],{"path":581,"title":582,"categories":583,"date":584},"\u002Fblog\u002Ffile-uploads-using-fetch-api-and-vuejs","File Uploads using Fetch API and VueJS",[276],"2021-11-23",{"path":586,"title":587,"categories":588,"date":589},"\u002Fblog\u002Ffilter-sort-and-search-arrays-with-javascript","Filter, Sort, and Search Arrays with JavaScript",[321],"2022-10-04",{"path":591,"title":592,"categories":593,"date":594},"\u002Fblog\u002Ffiltering-meilisearch-search-results-with-laravel-scout","Filtering Meilisearch Search Results with Laravel Scout",[282],"2022-04-11",{"path":596,"title":597,"categories":598,"date":599},"\u002Fblog\u002Ffiltering-vuejs-mixins","Filtering with VueJS Mixins",[282],"2017-12-07",{"path":601,"title":602,"categories":603,"date":605},"\u002Fblog\u002Fflash-data-with-inertiajs","Flash Data with InertiaJS",[604],"inertiajs","2025-07-31",{"path":607,"title":608,"categories":609,"date":610},"\u002Fblog\u002Fgenerating-wireguard-qr-codes-for-fast-mobile-deployments","Generating WireGuard QR codes for fast mobile deployments",[300],"2020-05-25",{"path":612,"title":613,"categories":614,"date":615},"\u002Fblog\u002Fgeocode-address-google-maps","Geocode An Address With Google Maps",[282],"2017-11-02",{"path":617,"title":618,"categories":619,"date":620},"\u002Fblog\u002Fget-active-element-with-javascript","Get Active Element with JavaScript",[321],"2022-10-18",{"path":622,"title":623,"categories":624,"date":625},"\u002Fblog\u002Fgetting-started-with-wireguard-vpn-important-concepts","Getting started with WireGuard VPN: Important Concepts",[300],"2020-05-21",{"path":627,"title":628,"categories":629,"date":630},"\u002Fblog\u002Fgoodbye-wordpress-hello-new-cms","Goodbye WordPress, Hello New CMS",[275,321],"2025-06-10",{"path":632,"title":633,"categories":634,"date":635},"\u002Fblog\u002Fgoogle-analytics-vue-router-single-page-application","Google Analytics with Vue Router in an SPA",[276],"2017-12-18",{"path":637,"title":638,"categories":639,"date":640},"\u002Fblog\u002Fhow-to-configure-a-wireguard-android-vpn-client","How to configure a WireGuard Android VPN Client",[300],"2020-05-27",{"path":642,"title":643,"categories":644,"date":645},"\u002Fblog\u002Fhow-to-configure-a-wireguard-ios-client","How to configure a WireGuard iOS client",[300],"2020-05-26",{"path":647,"title":648,"categories":649,"date":650},"\u002Fblog\u002Fhow-to-configure-a-wireguard-macos-client","How to configure a WireGuard macOS client",[300],"2020-05-24",{"path":652,"title":653,"categories":654,"date":655},"\u002Fblog\u002Fhow-to-configure-a-wireguard-windows-10-vpn-client","How to configure a WireGuard Windows 10 VPN client",[300],"2020-05-23",{"path":657,"title":658,"categories":659,"date":660},"\u002Fblog\u002Fhow-to-get-ssh-to-work-with-1password-docker-desktop-macos-within-a-container","How to Get SSH to Work with 1Password, Docker Desktop, and macOS Within a Container",[293],"2024-10-11",{"path":662,"title":663,"categories":664,"date":665},"\u002Fblog\u002Fhow-to-preload-css-background-images","How to Preload CSS Background Images",[321],"2018-09-27",{"path":667,"title":668,"categories":669,"date":670},"\u002Fblog\u002Fhow-to-set-up-wireguard-vpn-server-on-ubuntu-20-04","How to set up WireGuard VPN server on Ubuntu 20.04",[293,300],"2020-05-22",{"path":672,"title":673,"categories":674,"date":504},"\u002Fblog\u002Fimplementing-tagging-component","Implementing the Vue JS Tag Component",[276],{"path":676,"title":677,"categories":678,"date":679},"\u002Fblog\u002Fimportance-of-repeatable-environments","Importance of Repeatable Environments",[413],"2026-03-20",{"path":681,"title":682,"categories":683,"date":684},"\u002Fblog\u002Fimporting-and-using-components-in-nuxt-3","Importing and Using Components in Nuxt 3",[275],"2022-04-19",{"path":686,"title":687,"categories":688,"date":689},"\u002Fblog\u002Finstall-unifi-video-controller-on-synology-nas-using-docker","Install UniFi Video Controller on Synology NAS using Docker",[299,554],"2018-08-29",{"path":691,"title":692,"categories":693,"date":694},"\u002Fblog\u002Finstalling-and-preparing-your-synology-server","Installing and preparing your Synology Server",[554,300],"2018-06-23",{"path":696,"title":697,"categories":698,"date":699},"\u002Fblog\u002Finstalling-configuring-laravel-passport","Installing and Configuring Laravel Passport",[282],"2017-09-25",{"path":701,"title":702,"categories":703,"date":704},"\u002Fblog\u002Finstalling-configuring-laravel-socialite","Installing And Configuring Laravel Socialite",[282],"2017-09-05",{"path":706,"title":707,"categories":708,"date":458},"\u002Fblog\u002Finstalling-configuring-laravel-spa","Installing and Configuring Laravel For a Single Page Application",[282],{"path":710,"title":711,"categories":712,"date":438},"\u002Fblog\u002Finstalling-laravel-cashier-on-laravel-6-x","Installing Laravel Cashier on Laravel 6.x",[282],{"path":714,"title":715,"categories":716,"date":717},"\u002Fblog\u002Finstalling-tailwindcss-in-a-wordpress-theme","Installing TailwindCSS in a WordPress Theme",[321],"2020-08-06",{"path":719,"title":720,"categories":721,"date":722},"\u002Fblog\u002Fintroducing-benchkit-laravel-performance-testing-tool","Introducing BenchKit: The Laravel Performance Testing Tool We've All Been Waiting For",[293,282],"2025-09-25",{"path":724,"title":725,"categories":726,"date":727},"\u002Fblog\u002Fintroduction-to-classes-and-oop-with-javascript","Introduction to Classes and OOP with JavaScript",[321],"2022-09-20",{"path":729,"title":730,"categories":731,"date":555},"\u002Fblog\u002Flaravel-admin-routes-and-security-in-a-spa","Laravel Admin Routes and Security in a SPA",[282],{"path":733,"title":734,"categories":735,"date":736},"\u002Fblog\u002Flaravel-gates-and-policies-in-an-api-driven-spa","Laravel Gates and Policies in an API Driven SPA",[282],"2018-08-21",{"path":738,"title":739,"categories":740,"date":741},"\u002Fblog\u002Flaravel-hcaptcha-custom-validation-rule","Laravel hCaptcha Custom Validation Rule",[282],"2020-07-06",{"path":743,"title":744,"categories":745,"date":746},"\u002Fblog\u002Flaravel-one-to-many-deployments-with-docker-ansible","Laravel: One-to-many Deployments with Docker + Ansible",[293,282],"2023-04-26",{"path":748,"title":749,"categories":750,"date":751},"\u002Fblog\u002Fmanaging-pivot-data-with-laravel-eloquent","Managing Pivot Data with Laravel Eloquent",[282],"2022-10-25",{"path":753,"title":754,"categories":755,"date":438},"\u002Fblog\u002Fmanaging-stripe-payment-methods-in-vuejs-spa-and-laravel-api","Managing Stripe Payment Methods in VueJS SPA and Laravel API",[282,276],{"path":757,"title":758,"categories":759,"date":545},"\u002Fblog\u002Fmany-many-relationships-laravel","Many To Many Relationships With Laravel",[282],{"path":761,"title":762,"categories":763,"date":327},"\u002Fblog\u002Fmedia-temple-amazon-s3-backup-and-other-linux-servers","Media Temple Amazon S3 Backup for DV (this works on other Linux servers too)",[293],{"path":765,"title":766,"categories":767,"date":768},"\u002Fblog\u002Fmigrating-from-wordpress-to-nuxt-content","Migrating from WordPress to Nuxt Content",[275],"2025-09-04",{"path":770,"title":771,"categories":772,"date":388},"\u002Fblog\u002Fmigrating-layouts-from-nuxt-2-to-nuxt-3","Migrating Layouts from Nuxt 2 to Nuxt 3",[275],{"path":774,"title":775,"categories":776,"date":777},"\u002Fblog\u002Fnginx-unit-fixing-value-doesnt-exist","NGINX Unit: Fixing \"Value doesn't exist.\"",[293,524],"2023-10-24",{"path":779,"title":780,"categories":781,"date":782},"\u002Fblog\u002Fpackaging-a-nuxtjs-app-for-ios-and-android-with-capacitorjs","Packaging a NuxtJS app for iOS and Android with CapacitorJS",[275],"2020-08-16",{"path":784,"title":785,"categories":786,"date":787},"\u002Fblog\u002Fpartial-reloads-with-inertiajs","Partial Reloads with InertiaJS",[604],"2024-01-20",{"path":789,"title":790,"categories":791,"date":792},"\u002Fblog\u002Fpassword-security-validation-with-vuejs-and-zxcvbn","Password Security Validation with VueJS and zxcvbn",[604],"2020-05-06",{"path":794,"title":795,"categories":796,"date":797},"\u002Fblog\u002Fplanning-your-laravel-and-vuejs-spa-application-admin-section","Planning your Laravel and VueJS SPA Application Admin Section",[282],"2018-08-14",{"path":799,"title":800,"categories":801,"date":802},"\u002Fblog\u002Fpost-put-and-patch-requests-with-nuxt-3","POST, PUT, and PATCH Requests with Nuxt 3",[275],"2022-05-03",{"path":804,"title":805,"categories":806,"date":717},"\u002Fblog\u002Fpost-put-patch-requests-with-vuejs-and-axios","POST, PUT & PATCH Requests with VueJS and Axios",[276],{"path":808,"title":809,"categories":810,"date":811},"\u002Fblog\u002Fprevent-website-downtime-cloud-outage-survival-guide","Prevent Website Downtime with this Cloud Outage Survival Guide",[293],"2016-12-05",{"path":813,"title":814,"categories":815,"date":530},"\u002Fblog\u002Fpreview-file-uploads-with-axios-and-vuejs","Preview File Uploads with Axios and VueJS",[276],{"path":817,"title":818,"categories":819,"date":820},"\u002Fblog\u002Fpreview-mp3-with-html5-audio-element-and-vuejs","Preview MP3 with HTML5 Audio Element and VueJS",[276],"2021-10-19",{"path":822,"title":823,"categories":824,"date":825},"\u002Fblog\u002Fpreview-video-before-uploading-with-html5-and-vuejs","Preview Video Before Uploading with HTML5 and VueJS",[276],"2021-10-25",{"path":827,"title":828,"categories":829,"date":830},"\u002Fblog\u002Fpreviewing-a-csv-file-with-vuejs-and-papaparse","Previewing a CSV file with VueJS and Papaparse",[276],"2021-11-01",{"path":832,"title":833,"categories":834,"date":835},"\u002Fblog\u002Fpublic-private-api-laravel","Public and Private API with Laravel",[282],"2017-12-28",{"path":837,"title":838,"categories":839,"date":840},"\u002Fblog\u002Fre-using-vuejs-mixins-filtering-google-map-data","Re-using VueJS Mixins and Filtering Google Map Data",[276],"2017-12-11",{"path":842,"title":843,"categories":844,"date":845},"\u002Fblog\u002Froast-designs-have-been-updated","Roast Designs Have Been Updated",[276,282],"2018-05-16",{"path":847,"title":848,"categories":849,"date":850},"\u002Fblog\u002Froast-is-on-laravel-5-6","Roast is on Laravel 5.6!",[276,282],"2018-06-13",{"path":852,"title":853,"categories":854,"date":855},"\u002Fblog\u002Fsearch-eloquent-relationships-with-laravel-scout-and-meilisearch","Search Eloquent Relationships with Laravel Scout and Meilisearch",[282],"2022-03-28",{"path":857,"title":858,"categories":859,"date":860},"\u002Fblog\u002Fsending-post-put-and-patch-requests-with-fetch-api-and-vuejs","Sending POST, PUT, and PATCH Requests with Fetch API and VueJS",[276],"2021-11-15",{"path":862,"title":863,"categories":864,"date":865},"\u002Fblog\u002Fsending-server-sent-events-with-laravel","Sending Server Sent Events (SSE) with Laravel",[282],"2025-12-09",{"path":867,"title":868,"categories":869,"date":448},"\u002Fblog\u002Fset-song-played-percentage-amplitudejs","Set Song Played Percentage with AmplitudeJS",[321],{"path":871,"title":872,"categories":873,"date":874},"\u002Fblog\u002Fsetting-page-titles-in-nuxt-3","Setting Page Titles in Nuxt 3",[275],"2022-03-22",{"path":876,"title":877,"categories":878,"date":879},"\u002Fblog\u002Fsorting-in-vuejs-components-and-vuex-state","Sorting in VueJS Components and Vuex State",[275],"2018-09-25",{"path":881,"title":882,"categories":883,"date":884},"\u002Fblog\u002Fsorting-meilisearch-results-with-laravel-scout-and-eloquent","Sorting Meilisearch Results with Laravel Scout and Eloquent",[282],"2022-04-04",{"path":886,"title":887,"categories":888,"date":889},"\u002Fblog\u002Fspa-tutorial-update-2","SPA Tutorial Update 2",[282,276],"2017-11-13",{"path":891,"title":892,"categories":893,"date":894},"\u002Fblog\u002Fspa-tutorial-update-3","SPA Tutorial Update 3",[282,276],"2017-12-04",{"path":896,"title":897,"categories":898,"date":899},"\u002Fblog\u002Fspa-tutorial-update-4","SPA Tutorial Update 4",[282,276],"2017-12-21",{"path":901,"title":902,"categories":903,"date":904},"\u002Fblog\u002Fspin-launch-deploy-laravel-like-a-pro","Spin v3: Deploy Laravel Like A Pro",[293,282],"2025-03-19",{"path":906,"title":907,"categories":908,"date":909},"\u002Fblog\u002Fstream-terminal-output-to-browser","Stream Terminal Output To Browser",[282],"2025-12-16",{"path":911,"title":912,"categories":913,"date":914},"\u002Fblog\u002Fstructuring-vue-2-vue-router-vuex-single-page-application","Structuring Vue 2, Vue Router, Vuex for a Single Page Application",[276],"2017-10-02",{"path":916,"title":917,"categories":918,"date":919},"\u002Fblog\u002Fstyle-the-html-5-audio-element","Tutorial: How To Style the HTML 5 Audio Player",[321],"2012-10-21",{"path":921,"title":922,"categories":923,"date":924},"\u002Fblog\u002Ftagging-with-laravel","Tagging With Laravel",[321],"2017-11-27",{"path":926,"title":927,"categories":928,"date":398},"\u002Fblog\u002Ftutorial-progress","SPA Tutorial Progress Update",[282],{"path":930,"title":931,"categories":932,"date":933},"\u002Fblog\u002Funique-elements-in-amplitudejs","Unique Elements in AmplitudeJS",[321],"2018-09-06",{"path":935,"title":936,"categories":937,"date":938},"\u002Fblog\u002Fupgrade-your-ubiquiti-usg-firmware-the-easy-way","Upgrade your Ubiquiti USG firmware the easy way",[299,300],"2017-12-01",{"path":940,"title":941,"categories":942,"date":943},"\u002Fblog\u002Fuploading-files-vuejs-axios","Uploading Files With VueJS and Axios",[276],"2017-12-13",{"path":945,"title":946,"categories":947,"date":948},"\u002Fblog\u002Furl-query-parameters-with-javascript-vue-2-and-vue-3","URL Query Parameters with JavaScript, Vue 2 and Vue 3",[276],"2022-09-06",{"path":950,"title":951,"categories":952,"date":953},"\u002Fblog\u002Fusing-amplitudejs-public-methods","Using AmplitudeJS Public Methods",[276],"2018-09-13",{"path":955,"title":956,"categories":957,"date":277},"\u002Fblog\u002Fusing-async-data-in-nuxt-3","Using asyncData in Nuxt 3",[275],{"path":959,"title":960,"categories":961,"date":443},"\u002Fblog\u002Fusing-axios-to-make-api-requests-with-vuejs","Using Axios to Make API Requests With VueJS",[276],{"path":963,"title":964,"categories":965,"date":874},"\u002Fblog\u002Fusing-environment-variables-in-nuxt-3","Using Environment Variables in Nuxt 3",[275],{"path":967,"title":968,"categories":969,"date":970},"\u002Fblog\u002Fusing-foundation-sites-laravel-5-3-webpack","Using Foundation Sites with Laravel 5.3 and Webpack",[282],"2017-02-24",{"path":972,"title":973,"categories":974,"date":717},"\u002Fblog\u002Fusing-laravel-mix-in-a-wordpress-theme","Using Laravel Mix in a WordPress Theme",[321],{"path":976,"title":977,"categories":978,"date":979},"\u002Fblog\u002Fusing-laravel-sanctum-airlock-with-nuxtjs","Using Laravel Sanctum\u002FAirlock with NuxtJS",[282],"2020-01-17",{"path":981,"title":982,"categories":983,"date":782},"\u002Fblog\u002Fusing-nuxtjs-to-build-an-ios-and-android-app","Using NuxtJS to Build an iOS and Android App",[275],{"path":985,"title":986,"categories":987,"date":398},"\u002Fblog\u002Fusing-sass-vue-components-laravel-mix","Using SASS in Vue Components with Laravel Mix",[276],{"path":989,"title":990,"categories":991,"date":438},"\u002Fblog\u002Fusing-stripe-elements-in-a-vuejs-component","Using Stripe Elements in a VueJS Component",[276],{"path":993,"title":994,"categories":995,"date":782},"\u002Fblog\u002Fusing-tailwindcss-to-design-your-mobile-app","Using TailwindCSS to Design Your Mobile App",[276],{"path":997,"title":998,"categories":999,"date":1000},"\u002Fblog\u002Fusing-vuex-modules-inside-components","Using Vuex Modules Inside Components",[276],"2017-10-19",{"path":1002,"title":1003,"categories":1004,"date":1005},"\u002Fblog\u002Fusing-vuex-with-inertiajs","Using Vuex with InertiaJS",[604],"2021-09-21",{"path":1007,"title":1008,"categories":1009,"date":1010},"\u002Fblog\u002Fvalidating-api-request-javascript-laravel","Validating an API request with Javascript and Laravel",[282,321],"2017-10-30",{"path":1012,"title":1013,"categories":1014,"date":874},"\u002Fblog\u002Fvue-3-web-notification-component","Vue 3 Web Notification Component",[276],{"path":1016,"title":1017,"categories":1018,"date":504},"\u002Fblog\u002Fvue-js-tag-input","Vue JS Tag input",[276],{"path":1020,"title":1021,"categories":1022,"date":1023},"\u002Fblog\u002Fvue-router-navigation-guards-vuex","Vue Router Navigation Guards with Vuex",[276],"2018-01-08",{"path":1025,"title":1026,"categories":1027,"date":1028},"\u002Fblog\u002Fvue-router-permission-recipes-and-laravel-policies-examples","Vue Router Permission Recipes and Laravel Policies Examples",[276],"2018-09-18",{"path":1030,"title":1031,"categories":1032,"date":1033},"\u002Fblog\u002Fvuejs-app-admin-screens","VueJS App Admin Screens",[276],"2018-09-11",{"path":1035,"title":1036,"categories":1037,"date":1038},"\u002Fblog\u002Fvuejs-route-permissions-security-and-admin-section","VueJS Route Permissions, Security and Admin Section",[276],"2018-09-04",{"path":1040,"title":1041,"categories":1042,"date":393},"\u002Fblog\u002Fvuepress-within-a-laravel-application","Vuepress Within a Laravel Application",[276],{"path":1044,"title":1045,"categories":1046,"date":1047},"\u002Fblog\u002Fwhat-is-amplitude-js","What Is AmplitudeJS?",[321],"2018-08-22",{"path":1049,"title":1050,"categories":1051,"date":1052},"\u002Fblog\u002Fwhen-api-driven-development-works-and-when-it-doesnt","When API Driven Development Works... And When It Doesn't",[282],"2020-01-10",{"path":1054,"title":1055,"categories":1056,"date":1057},"\u002Fblog\u002Fwhy-make-your-saas-self-hostable","Why Make Your SaaS Self-Hostable?",[413],"2026-01-28",{"path":1059,"title":1060,"categories":1061,"date":1047},"\u002Fblog\u002Fworking-with-amplitudejs-song-object-metadata","Working With AmplitudeJS Song Object Metadata",[321],[1063,1067,1071,1075,1079,1083,1087,1090,1094,1098,1102],{"path":1064,"title":1065,"categories":1066},"\u002Fguides\u002Famplitudejs-from-the-ground-up","AmplitudeJS From the Ground Up",[321],{"path":1068,"title":1069,"categories":1070},"\u002Fguides\u002Fapi-driven-development-laravel-vuejs","API Driven Development With Laravel and VueJS",[282,276],{"path":1072,"title":1073,"categories":1074},"\u002Fguides\u002Fcomplete-ubiquiti-unifi-synology-network-build","Complete Ubiquiti UniFi + Synology Network Build",[299,300,554],{"path":1076,"title":1077,"categories":1078},"\u002Fguides\u002Fdesigning-your-saas-app-to-be-self-hostable","Designing Your SaaS App to be Self-Hostable",[413],{"path":1080,"title":1081,"categories":1082},"\u002Fguides\u002Fgain-flexibility-and-increase-privacy-with-wireguard-vpn","Gain Flexibility and Increase Privacy with WireGuard VPN",[300],{"path":1084,"title":1085,"categories":1086},"\u002Fguides\u002Fguide-uploading-files-vuejs-axios","Guide to Uploading Files with VueJS and Axios",[321,276],{"path":1088,"title":982,"categories":1089},"\u002Fguides\u002Fhow-to-build-an-ios-android-app-with-capacitorjs-nuxtjs-tailwindcss",[321,276,275],{"path":1091,"title":1092,"categories":1093},"\u002Fguides\u002Fupgrading-nuxt-2-to-nuxt-3","Upgrading Nuxt 2 to Nuxt 3",[275,276],{"path":1095,"title":1096,"categories":1097},"\u002Fguides\u002Fusing-fetch-api-with-vuejs","Using Fetch API with Vue.js",[321,276],{"path":1099,"title":1100,"categories":1101},"\u002Fguides\u002Fusing-laravel-cashier-with-vuejs-spa-and-laravel-passport-api","Using Laravel Cashier with VueJS SPA and Laravel Passport API",[321,276],{"path":1103,"title":1104,"categories":1105},"\u002Fguides\u002Fyour-guide-to-using-an-api-with-vuejs-nuxtjs-and-axios","Your Guide to Using an API with VueJS\u002FNuxtJS and Axios",[321,276],{"id":1107,"title":507,"author":1108,"body":1109,"categories":1209,"date":509,"description":1210,"extension":16,"guide":1211,"header_image":92,"meta":1212,"navigation":17,"path":506,"seo":1213,"stem":1214,"video_url":7,"__hash__":1215},"blog\u002Fblog\u002Fdesigning-an-installation-script.md","danpastori",{"type":22,"value":1110,"toc":1201},[1111,1115,1120,1123,1127,1130,1133,1137,1140,1144,1162,1165,1181,1184,1188,1191,1194,1198],[1112,1113,1114],"p",{},"A whole article for an installation script. Really? Yup. I believe this is probably one of the most important pieces of your application design. The goal is to make your app self-hostable and make it easy. An installation script makes it super easy for a user to get off the ground. Everything is set up and ready to go in the most minimal amount of steps.",[1116,1117,1119],"h2",{"id":1118},"what-to-include","What to Include",[1112,1121,1122],{},"Basically, anything to get the application configured and off the ground as quickly and bulletproof as possible. This could super admin credentials, database seeding and migrations, essential settings, etc. Anything the user needs to see the GUI of your app, assuming it's GUI based.",[1116,1124,1126],{"id":1125},"what-not-to-include","What NOT to Include",[1112,1128,1129],{},"Onboarding. The goal is to get TO the onboarding. What's the difference? The installation script should simply get the app running so when the user visits the URL of the application, they see something that's not an error screen. Onboarding is the UI the user will see to guide them through the rest of the set up.",[1112,1131,1132],{},"Think of the installation script as whatever it takes to power on your app. Once it's powered on, then you can design an onboarding wizard.",[1116,1134,1136],{"id":1135},"prompting-for-information","Prompting for Information",[1112,1138,1139],{},"If there is essential information and settings, it may be helpful to prompt for this within your installation script. Don't prompt for too much, that will be in the onboarding. Think within the context of \"super admin\" permissions. Whatever can set up the super admin logged in is what we need to prompt for.",[1116,1141,1143],{"id":1142},"example-with-bugflow","Example with Bugflow",[1112,1145,1146,1147,1152,1153,1157,1158,1161],{},"We just went through this process with ",[1148,1149,37],"a",{"href":54,"rel":1150},[1151],"nofollow",". Once the user pulls down the docker image, they need to run ",[1154,1155,1156],"code",{},"php artisan bugflow:init",". We use Laravel, so it's an ",[1154,1159,1160],{},"artisan"," command.",[1112,1163,1164],{},"With this command, we:",[1166,1167,1168,1172,1175,1178],"ol",{},[1169,1170,1171],"li",{},"Configure the app key. This is essential for Laravel and encryption.",[1169,1173,1174],{},"Set up the database and migrate all the necessary tables.",[1169,1176,1177],{},"Initialize a super admin registration token.",[1169,1179,1180],{},"Display a secure register link with this token to create a super admin user.",[1112,1182,1183],{},"It's a simple script that's absolutely essential. It's also scalable. Instead of giving individual commands to initialize, we give one command that sets up everything. If we add more features and need more configuration, we can easily extend this command. Finally, once everything is set up, we create a unique token and print out a signed authentication URL in the terminal. The user can go to that URL and enter their super admin credentials in the UI. We hand off the rest of the onboarding from there.",[1116,1185,1187],{"id":1186},"update-script","Update Script",[1112,1189,1190],{},"Another pain point of managing a self-hosted application is running updates. We always provide an update script that the user can run that will take care of any of the data migrations or setting re-configuration automatically.",[1112,1192,1193],{},"With this script, we also prompt for questions to help guide the update. Maybe the user wants to format a migration a certain way, or move data in to this place instead of that. If there are options available like that, we prompt the user. Anything to take the pain out of running a series of commands.",[1116,1195,1197],{"id":1196},"wrapping-up","Wrapping Up",[1112,1199,1200],{},"Self-hosting can be intimidating so making installation and maintenance seamless as possible will help you retain satisfied users. You don't want your users starting off on the wrong foot with an error screen and a bunch of rage. It's exciting to own your data and keeping people excited is part of the process. A simple installation script and organized update script make your app feel so much more polished and put together.",{"title":25,"searchDepth":26,"depth":26,"links":1202},[1203,1204,1205,1206,1207,1208],{"id":1118,"depth":26,"text":1119},{"id":1125,"depth":26,"text":1126},{"id":1135,"depth":26,"text":1136},{"id":1142,"depth":26,"text":1143},{"id":1186,"depth":26,"text":1187},{"id":1196,"depth":26,"text":1197},[413],"Getting your users up and running as quickly as possible is essential to creating a self-hostable application. The experience will start the user off on the right foot and gain traction for your app.","\u002Fguides\u002Fdesigning-your-saas-app-to-be-self-hostable\u002F",{},{"title":507,"description":1210},"blog\u002Fdesigning-an-installation-script","abHJBa8O2vuKM7l3BZ5ij4woKhrU3TCBt256oK7-jaI",{"id":1217,"title":1077,"author":1108,"body":1218,"categories":1289,"description":1290,"extension":16,"header_image":92,"meta":1291,"navigation":17,"path":1076,"posts":1292,"published":17,"seo":1293,"soon":1294,"stem":1301,"video_url":7,"__hash__":1302},"guides\u002Fguides\u002Fdesigning-your-saas-app-to-be-self-hostable.md",{"type":22,"value":1219,"toc":1283},[1220,1223,1227,1230,1238,1244,1248,1251,1255,1258,1262,1269,1277,1280],[1112,1221,1222],{},"We are all in on designing our SaaS apps to also be self-hostable. Privacy, owning your data, compliances (HIPPA, FINRA), price, etc. All of these factors make the ability to self-host your apps extremely enticing for customers and businesses. This series will hopefully shed some light on why we are all in while providing some insight on how to make your own SaaS apps self-hostable.",[1116,1224,1226],{"id":1225},"what-is-a-self-hostable-app","What is a Self-Hostable App?",[1112,1228,1229],{},"Just to set a starting point and some definitions for the entire course, it'd be good to start with the basics. What defines a self-hostable application? Well, I didn't look up a \"formal\" definition, so this is what we are going with.",[1112,1231,1232,1233,1237],{},"A self-hostable application is a web application, SaaS, or service that your customers, can host on any server anywhere you want. This could be in a data center, on your laptop, or in your basement. A self-hostable application allows the customer to run the features of the code, but gives the customer ",[1234,1235,1236],"strong",{},"ownership"," of the data.",[1112,1239,1240,1241,1243],{},"The last piece is huge, ",[1234,1242,1236],{}," of the data. A key selling point\u002Fmindset of even designing a self-hostable application is that the customer owns their own data.",[1116,1245,1247],{"id":1246},"the-goal-of-this-series","The Goal of This Series",[1112,1249,1250],{},"Let's be real, we are in the age of AI. Writing \"how-to\" tech tutorials is a waste of time. You can literally ask the latest model and get everything you need within the context of your app. This series is not a tech tutorial, it's a guide on the thought process of designing your SaaS app to be self-hostable. The goal of this series is to help you think through the design concepts, steps and onboarding to add a self-hostable component to your existing SaaS or start from the ground up.",[1116,1252,1254],{"id":1253},"what-language-do-i-need-to-learn","What Language Do I Need to Learn",[1112,1256,1257],{},"Doesn't matter. If you use Laravel, Ruby on Rails, Python, JavaScript, you will be able to follow these principles. I may reference Laravel since that's what we use to make our apps, but the concepts will work across any language or platform.",[1116,1259,1261],{"id":1260},"so-do-we-make-self-hostable-apps","So, do we make self-hostable apps?",[1112,1263,1264,1265,1268],{},"I wouldn't be writing this if didn't. Over the last couple years we've been learning, building, and polishing our own products to be self-hostable. Our first launch is going to be ",[1148,1266,37],{"href":54,"rel":1267},[1151],", a customer feedback application that we launched awhile back. Making this app self-hostable has taught us so much about design and user onboarding, and can't wait to share with you.",[1112,1270,1271,1272,1276],{},"We also have ",[1148,1273,6],{"href":1274,"rel":1275},"https:\u002F\u002Fselfhostpro.com\u002F",[1151],". This is a licensing service that allows you to sell your Dockerized self-hostable apps to customers easily and securely. We provide the security and ability to market and sell your app. And yes, if you want to self-host Self-Host Pro, you can!",[1112,1278,1279],{},"So yes, we make self-hostable apps. Both of these apps are your standard SaaS app, but are also capable to be self-hosted. We've learned a lot developing these apps and the mindset on how to build these apps. You will hear a lot more about these apps through out the series along with the other apps we have in the pipeline.",[1112,1281,1282],{},"Let's get started!",{"title":25,"searchDepth":26,"depth":26,"links":1284},[1285,1286,1287,1288],{"id":1225,"depth":26,"text":1226},{"id":1246,"depth":26,"text":1247},{"id":1253,"depth":26,"text":1254},{"id":1260,"depth":26,"text":1261},[413],"Learn how to design your SaaS to be self-hosted by customers and businesses",{},[1054,676,410,506],{"title":1077,"description":1290},[1295,1296,1297,1298,1299,1300],"OAuth and other authentication methods","Building for a Super Admin","Permissions and Multi-Tenant Design","Deploying the Application Everywhere","Monetizing your Self-Hostable Application","...and a whole heck of a lot more","guides\u002Fdesigning-your-saas-app-to-be-self-hostable","WKp0GgBzG_8d-j9wiHaLhlxA1t29RDxHNPktXs7kmSw",[1304,1375,1470,1609],{"id":1305,"title":1055,"author":1108,"body":1306,"categories":1369,"date":1057,"description":1370,"extension":16,"guide":1211,"header_image":92,"meta":1371,"navigation":17,"path":1054,"seo":1372,"stem":1373,"video_url":7,"__hash__":1374},"blog\u002Fblog\u002Fwhy-make-your-saas-self-hostable.md",{"type":22,"value":1307,"toc":1363},[1308,1311,1315,1322,1332,1336,1339,1343,1346,1350,1353,1360],[1112,1309,1310],{},"So I bet there's a ton of questions, like \"Why would you want to make your app self-hostable?\" And \"Isn’t it just “more-work?” \"What about the monetary return of selling SAAS\"? Well, they are all very valid. All things I've considered, thought about, wrote about and researched so let’s break them down.",[1116,1312,1314],{"id":1313},"why-would-you-want-to-make-your-app-self-hostable","Why Would You Want to Make Your App Self-Hostable?",[1112,1316,1317,1318,1321],{},"Let's start with the biggest question of them all, \"Why\"? ",[1234,1319,1320],{},"Building a self-hostable opens up a world of opportunity and different markets",". You are in full control of the distribution and can design it as such.",[1112,1323,1324,1325,1331],{},"Before we even dive into this, realize that making your app self-hostable does not mean you are making an app that can not be hosted as a SAAS in the cloud. It's the opposite. You are making a SAAS that can be hosted in ",[1326,1327,1328],"em",{},[1234,1329,1330],{},"any"," cloud. That means you can host your SAAS, but so can others. Wouldn't this hurt your bottom line? Affect MRR? Destroy the primary purpose of building a SAAS? Not if you design your business in a way where it's complimentary. And that's what I mean about opening up a world of opportunity and different markets.",[1116,1333,1335],{"id":1334},"isnt-this-just-more-work","Isn't this just more work?",[1112,1337,1338],{},"Overall, not really. You will have to make the configuration and environment variables for your app accessible to the Super Admin instead of in a file. You will also have to focus on the user experience of setting up your app. Other than that, just build! I've found that working through some of the Super Admin settings actually makes managing your SAAS easier for yourself. So in the long run, it might be good to run through for your cloud based SAAS as well.",[1116,1340,1342],{"id":1341},"will-i-lose-mrr","Will I Lose MRR?",[1112,1344,1345],{},"We will focus a lot on this when we run through monetizing your application. I won't be going through the marketing for a traditional SAAS, but how to make money on a self-hosted model. We will discuss the possibility of a low overhead business to customer (B2C) app with community support. A buy once model (thanks Basecamp!) and the math behind it. And how to keep your standard SAAS but offer a self-hosted version where security and privacy are the upmost concern.",[1116,1347,1349],{"id":1348},"b2c-apps","B2C Apps",[1112,1351,1352],{},"One point I really want to hit with designing a self-hostable application is the opening up B2C marketplace. B2C is hard. Most indie-hackers, product developers avoid it like the plague and I don't blame them. Individuals have less money than businesses and usually require more features before purchasing. I think this is where self-hosted applications can really shine. You could do a buy once model and let the customer host it and you provide the support.",[1112,1354,1355,1356,1359],{},"Think about it this way. Say you are writing a cloud only SAAS for customers. It's $10\u002Fmo. If you can onboard customers, great, but now you have to think of server costs. Your app is successful, even better! Now you have to scale, provide the same level service, and hire people. What about churn? Lots of customers when times get tough cancel their subscriptions. You can't do a lifetime, one time price with a cloud based SAAS or you will find out ",[1326,1357,1358],{},"real quick"," what that threshold is where you lose money.",[1112,1361,1362],{},"Now let's reframe this. Say you sold your app once to a customer for $99. You don't have to scale it. You essentially bought 10 months of that user's subscription. You just have to release updates. Well, they have the choice to upgrade for a fee if they want, or continue running what they purchased. It's simple and saves you some headache!",{"title":25,"searchDepth":26,"depth":26,"links":1364},[1365,1366,1367,1368],{"id":1313,"depth":26,"text":1314},{"id":1334,"depth":26,"text":1335},{"id":1341,"depth":26,"text":1342},{"id":1348,"depth":26,"text":1349},[413],"Let's dive into why we would want to design our SaaS to be self-hostable.",{},{"title":1055,"description":1370},"blog\u002Fwhy-make-your-saas-self-hostable","aiij0mnxyy4S9IV2UfXOfVwk9Ah-9w7isVrRgErm5nU",{"id":1376,"title":677,"author":1108,"body":1377,"categories":1464,"date":679,"description":1465,"extension":16,"guide":1211,"header_image":92,"meta":1466,"navigation":17,"path":676,"seo":1467,"stem":1468,"video_url":7,"__hash__":1469},"blog\u002Fblog\u002Fimportance-of-repeatable-environments.md",{"type":22,"value":1378,"toc":1458},[1379,1382,1386,1389,1392,1395,1398,1401,1405,1417,1420,1423,1426,1429,1433,1436,1439,1442,1446],[1112,1380,1381],{},"Disposable, repeatable environments from development to production is something we are extremely passionate about. It becomes even more important when you are designing a self-hostable app. Let's understand why.",[1116,1383,1385],{"id":1384},"where-and-how-is-your-app-running","Where and how is your app running?",[1112,1387,1388],{},"If you work at a company, you might be issued a computer. Or you might have a choice for which type of machine you want, Windows, Mac, Linux, etc. Guess what. All of those computers will run your app differently.",[1112,1390,1391],{},"It gets better. What version of the programming language do each of your employees or co-workers have? You pull the latest update of a language and it introduces some really sweet upgrades. You push the code. A co-worker pulls the code, they are running an older version, there are errors.",[1112,1393,1394],{},"Even worse, you push your new code in a hot fix to production and it's running the outdated language on a different OS than you developed on. Well... You have more bugs.",[1112,1396,1397],{},"Next level, you make your app self-hostable. You have literally no control on where your app will run. Someoneon's Synology, an enterprise data center, a Raspberry Pi. All with different operating systems and versions. This is a nightmare.",[1112,1399,1400],{},"So what can you do?",[1116,1402,1404],{"id":1403},"controlling-your-infrastructure-with-docker","Controlling Your Infrastructure with Docker",[1112,1406,1407,1408,1411,1412,1416],{},"When we develop an app, we do EVERYTHING with Docker. Our development environments are run with ",[1148,1409,162],{"href":180,"rel":1410},[1151]," which is a \"syntax sugar\" on Docker Compose. We made our own ",[1148,1413,1415],{"href":158,"rel":1414},[1151],"Docker container for PHP"," to standardize how it's run. Everything is disposable and repeatable.",[1112,1418,1419],{},"I know when I push code and Jay pulls it down on his local machine, he is getting the code to operate the exact way that I wrote it. Node and PHP versions are the same. It's running the same OS. The amount of headaches and unexpected bug fixes are gone.",[1112,1421,1422],{},"There are so many advantages to Docker and it works across all languages and platforms. If you want to pull in some cool new technology into your app, you can configure it through your Docker file and have it up and running when you bring your app up.",[1112,1424,1425],{},"If having standardized development environments isn't exciting enough, you can ensure your app will run the same in production no matter what platform it's deployed to. This is why we choose Docker when designing our self-hostable applications.",[1112,1427,1428],{},"If someone purchases your app and wants to run it on their own hardware, the amount of support tickets and configuration help is dramatically decreased.",[1116,1430,1432],{"id":1431},"learning-curve-for-docker","Learning Curve for Docker",[1112,1434,1435],{},"There are a few moving parts with Docker that present a little bit of a learning curve. You have to have the Docker engine installed on your computer for development and you have to think about configuring your servers through YML files and code then actual spinning up a VM or bare metal machine.",[1112,1437,1438],{},"It took me awhile to grasp what was happening when I first started using Docker. However, once it clicks, there's no going back. I'm super stoked that Jay is very well versed and was able to get our machines set up correctly, but after using it for some time it starts to make a heck of a lot more sense.",[1112,1440,1441],{},"Once you get your machine set up you can simply control your server version, programming language version and dependencies all through your Docker config file. Your app will run the same whether it's on development or production.",[1116,1443,1445],{"id":1444},"getting-help-with-docker","Getting Help with Docker",[1112,1447,1448,1449,1453,1454,1457],{},"If you are interested in designing a self-hostable app or have questions about setting up your machine to use Docker, definitely reach out. It's such an important piece of the puzzle and will save a ton of headaches as you begin to design your self hostable application. We are proud of our ",[1148,1450,1452],{"href":158,"rel":1451},[1151],"PHP Docker image"," and ",[1148,1455,162],{"href":180,"rel":1456},[1151],", but if you use Node, Rails, Python, etc. no worries, there's a Docker image for every language!",{"title":25,"searchDepth":26,"depth":26,"links":1459},[1460,1461,1462,1463],{"id":1384,"depth":26,"text":1385},{"id":1403,"depth":26,"text":1404},{"id":1431,"depth":26,"text":1432},{"id":1444,"depth":26,"text":1445},[413],"When you are designing your app to be self-hostable, you don't know where it will be hosted. We need to eliminate as many variables as possible and make the app repeatable",{},{"title":677,"description":1465},"blog\u002Fimportance-of-repeatable-environments","0oOzG-bHGNf2lAMAGGZOD185d8QUOejcI9P7zxAHYfs",{"id":1471,"title":411,"author":1108,"body":1472,"categories":1603,"date":414,"description":1604,"extension":16,"guide":1211,"header_image":92,"meta":1605,"navigation":17,"path":410,"seo":1606,"stem":1607,"video_url":7,"__hash__":1608},"blog\u002Fblog\u002Fchoosing-your-services-and-database.md",{"type":22,"value":1473,"toc":1592},[1474,1486,1490,1493,1496,1499,1503,1510,1515,1524,1527,1531,1534,1537,1541,1544,1547,1550,1553,1557,1560,1563,1570,1573,1577],[1112,1475,1476,1477,1480,1481,1485],{},"The goal of creating a self-hostable application is, well, allowing it to be self-hosted. That means also allowing ",[1326,1478,1479],{},"others"," to self host your app. It has to be straight forward to do and maintain. As mentioned in the previous article, ",[1148,1482,677],{"href":1483,"rel":1484},"https:\u002F\u002Fserversideup.net\u002Fblog\u002Fimportance-of-repeatable-environments\u002F",[1151],", we use Docker for everything and recommend you do the same. However, there are still some considerations when choosing the database and what services you include.",[1116,1487,1489],{"id":1488},"basic-considerations","Basic Considerations",[1112,1491,1492],{},"The power of Docker is amazing. You can add an extremely powerful full text, Meilisearch container to your project with minimal set up time. Configure your Docker compose file and you are off to the races.",[1112,1494,1495],{},"However, that Meilisearch container requires a lot more resources, disk space, and maintenance. Even if you are licensing your self-hostable app to a business, it might be overkill for what they are using. They might only have 10-15 users and want the quickest solution to get up and running.",[1112,1497,1498],{},"But that's one consideration. What if you want to re-use the same code for a cloud hosted version that does have a million users? These type of architecture and user decisions really have to come into play when designing your app. So what do you do? Well, there are options.",[1116,1500,1502],{"id":1501},"service-options-queues-search-etc","Service Options (Queues, Search, etc.)",[1112,1504,1505,1506,1509],{},"The consideration for full text searching is similar for other processes as well. When we approached designing ",[1148,1507,37],{"href":54,"rel":1508},[1151]," we ran into this issue with our queues. We wanted to keep our self-hosted version as small as possible so it's easier to distribute, maintain and deploy on their own hardware. Why? Because the use case for our customers who want to self-host won't run into the same usage scenarios as we do (if they do, we offer an enterprise self-hosted version).",[1511,1512,1514],"h3",{"id":1513},"architecture","Architecture",[1112,1516,1517,1518,1523],{},"For reference, before moving forward, we use ",[1148,1519,1522],{"href":1520,"rel":1521},"https:\u002F\u002Flaravel.com",[1151],"Laravel"," to build our apps, and they make these next steps a breeze. All connection information for services is handled through the ENV file. I'm sure other frameworks have this as well, but the beauty comes from the abstraction layer above. If we dispatch a queue job, like we do for every feedback request, Laravel reads the configuration connection and dispatches the job where it needs to go. If you wanted to use SQS, Horizon (Laravel's powerful queue runner), or just a database, the job gets routed where it needs to go.",[1112,1525,1526],{},"If you aren't using a framework that supports this architecture, I'd recommend developing something similar for your app. I'd recommend a data transfer object, a contract, and some env configuration that allows you to easily swap out what service you need depending on infrastructure and usage requirements.",[1511,1528,1530],{"id":1529},"how-we-solved-this","How We Solved This",[1112,1532,1533],{},"With Bugflow, on our cloud hosted version, we need to process multiple simultaneous queue jobs to ensure everything gets processed as quickly as possible. For self-hosted instances, we just use the database. This is all managed from our environment variable. That means we aren't bringing up an unnecessary server for a use-case that our customers don't have. It also means that users paying for our SaaS get the benefit of speedy feedback on our Horizon Queue.",[1112,1535,1536],{},"You can also solve this similarly for full text searching depending on the database you choose (see next section).",[1116,1538,1540],{"id":1539},"database-options","Database Options",[1112,1542,1543],{},"With Docker, the database is, guess what, another service and container! This means that when you bring up your app, you might have to bring up another service as well. However, the database is slightly different and depending on your use case, you could possibly remove the container entirely.",[1112,1545,1546],{},"Let's say you want to get an application as small as possible to make it as simple as possible to distribute. You need full text search, queuing, and a database. Forget cloud for now, you can do whatever you want since you manage the infrastructure, let's focus on self-hosting.",[1112,1548,1549],{},"The first thing I'd do is move all queued tasks to a \"jobs\" table on the database. This will eliminate the need for a queue runner and another overkill container. Next, I'd choose PostgreSQL or another database that supports full text indexing. You can then eliminate the huge overhead of another full text search engine running in your app. This is just a consideration! If your app heavily relies on natural language processing or super advanced indexing, this might not be worth it. However, you'd be surprised how far you can get!",[1112,1551,1552],{},"If you really want to cut down and won't have an app that requires an insanely powerful database, you can even use SQLite. This removes the need entirely for even a database container! You could literally get down to deploying just your app code to self-hostable instances. We actually do this with Bugflow as well. On self hosted instances, SQLite runs our database and our queues. It makes our deployment extremely light weight and easy to run anywhere.",[1116,1554,1556],{"id":1555},"building-for-cloud-and-self-hosted","Building for Cloud and Self-Hosted",[1112,1558,1559],{},"Now you've heard me talk about how we deploy to self-hosted environments and to cloud using the same code base. How does this work? Well, if you use Docker, it's a lot easier than you may think!",[1112,1561,1562],{},"The most important pre-requisite to make your life easier is to allow your databases and services to be configurable and not hard coded. I mentioned this earlier, but it's even more important if you want to re-use your code and scale in the cloud while also keeping your footprint small in a self-hosted environment.",[1112,1564,1565,1566,1569],{},"So how we do it with Bugflow and Docker is straight forward. All the configuration for our services and databases is held in the environment variables that are required for the app to run. The actual server configuration is set up in our ",[1154,1567,1568],{},"docker-compose.yml"," file. This means, when we set our queues to run on Laravel Horizon, we can configure the service to run on Horizon container in production. The Horizon container comes on line when we bring up our app.",[1112,1571,1572],{},"In the self-hosted version, we just have a database configuration file. We set our queues to run in the database. The combination of what services get deployed + how we access them are all managed through the environment variables and what gets brought up through Docker!",[1116,1574,1576],{"id":1575},"need-a-hand","Need A Hand?",[1112,1578,1579,1580,1585,1586,1591],{},"If you have questions about how to architect your application to make it easy to self-host ",[1148,1581,1584],{"href":1582,"rel":1583},"https:\u002F\u002Fserversideup.net\u002Fhire-us",[1151],"or want us to do it for you",", feel free to reach out. We've done the same approach for multiple clients that we've worked with and are well-versed in the process. Hop on ",[1148,1587,1590],{"href":1588,"rel":1589},"https:\u002F\u002Fserversideup.net\u002Fdiscord\u002F",[1151],"Discord"," and we'd love to lend a hand!",{"title":25,"searchDepth":26,"depth":26,"links":1593},[1594,1595,1600,1601,1602],{"id":1488,"depth":26,"text":1489},{"id":1501,"depth":26,"text":1502,"children":1596},[1597,1599],{"id":1513,"depth":1598,"text":1514},3,{"id":1529,"depth":1598,"text":1530},{"id":1539,"depth":26,"text":1540},{"id":1555,"depth":26,"text":1556},{"id":1575,"depth":26,"text":1576},[413],"To make your app easy to self-host, choosing your services, database and how you configure everything comes up for consideration. We will discuss our approach to solve some of these issues.",{},{"title":411,"description":1604},"blog\u002Fchoosing-your-services-and-database","UxNHmVJLuvEsW8fOSVHzgOLdgtNPAGW_nQ9WWiF0U7E",{"id":1107,"title":507,"author":1108,"body":1610,"categories":1671,"date":509,"description":1210,"extension":16,"guide":1211,"header_image":92,"meta":1672,"navigation":17,"path":506,"seo":1673,"stem":1214,"video_url":7,"__hash__":1215},{"type":22,"value":1611,"toc":1663},[1612,1614,1616,1618,1620,1622,1624,1626,1628,1630,1639,1641,1651,1653,1655,1657,1659,1661],[1112,1613,1114],{},[1116,1615,1119],{"id":1118},[1112,1617,1122],{},[1116,1619,1126],{"id":1125},[1112,1621,1129],{},[1112,1623,1132],{},[1116,1625,1136],{"id":1135},[1112,1627,1139],{},[1116,1629,1143],{"id":1142},[1112,1631,1146,1632,1152,1635,1157,1637,1161],{},[1148,1633,37],{"href":54,"rel":1634},[1151],[1154,1636,1156],{},[1154,1638,1160],{},[1112,1640,1164],{},[1166,1642,1643,1645,1647,1649],{},[1169,1644,1171],{},[1169,1646,1174],{},[1169,1648,1177],{},[1169,1650,1180],{},[1112,1652,1183],{},[1116,1654,1187],{"id":1186},[1112,1656,1190],{},[1112,1658,1193],{},[1116,1660,1197],{"id":1196},[1112,1662,1200],{},{"title":25,"searchDepth":26,"depth":26,"links":1664},[1665,1666,1667,1668,1669,1670],{"id":1118,"depth":26,"text":1119},{"id":1125,"depth":26,"text":1126},{"id":1135,"depth":26,"text":1136},{"id":1142,"depth":26,"text":1143},{"id":1186,"depth":26,"text":1187},{"id":1196,"depth":26,"text":1197},[413],{},{"title":507,"description":1210},[1675,1684,1693,1702,1711],{"id":5,"title":6,"badge":7,"badges":1676,"buy_url":7,"complete_package_url":7,"description":15,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":18,"logo_wide":19,"meta":1679,"screenshot":28,"short_description":29,"short_title":7,"stem":30,"trial_url":31,"type":32,"url":33,"__hash__":34},[1677,1678],{"label":10,"color":11},{"label":13,"color":14},{"body":1680},{"type":22,"value":1681,"toc":1682},[],{"title":25,"searchDepth":26,"depth":26,"links":1683},[],{"id":36,"title":37,"badge":7,"badges":1685,"buy_url":7,"complete_package_url":7,"description":42,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":43,"logo_wide":44,"meta":1688,"screenshot":50,"short_description":51,"short_title":7,"stem":52,"trial_url":53,"type":32,"url":54,"__hash__":55},[1686,1687],{"label":40,"color":11},{"label":13,"color":14},{"body":1689},{"type":22,"value":1690,"toc":1691},[],{"title":25,"searchDepth":26,"depth":26,"links":1692},[],{"id":57,"title":58,"badge":59,"badges":1694,"buy_url":65,"complete_package_url":7,"description":66,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":67,"logo_wide":68,"meta":1697,"screenshot":74,"short_description":75,"short_title":7,"stem":76,"trial_url":7,"type":32,"url":77,"__hash__":78},[1695,1696],{"label":40,"color":11},{"label":63,"color":64},{"body":1698},{"type":22,"value":1699,"toc":1700},[],{"title":25,"searchDepth":26,"depth":26,"links":1701},[],{"id":80,"title":81,"badge":7,"badges":1703,"buy_url":89,"complete_package_url":89,"description":90,"essentials_url":91,"extension":16,"external":92,"logo_component":93,"logo_square":94,"logo_wide":7,"meta":1706,"screenshot":100,"short_description":101,"short_title":102,"stem":103,"trial_url":7,"type":104,"url":105,"__hash__":106},[1704,1705],{"label":84,"color":85},{"label":87,"color":88},{"body":1707},{"type":22,"value":1708,"toc":1709},[],{"title":25,"searchDepth":26,"depth":26,"links":1710},[],{"id":108,"title":109,"badge":7,"badges":1712,"buy_url":116,"complete_package_url":116,"description":117,"essentials_url":118,"extension":16,"external":92,"logo_component":119,"logo_square":120,"logo_wide":7,"meta":1716,"screenshot":126,"short_description":127,"short_title":128,"stem":129,"trial_url":7,"type":104,"url":130,"__hash__":131},[1713,1714,1715],{"label":84,"color":85},{"label":87,"color":88},{"label":114,"color":115},{"body":1717},{"type":22,"value":1718,"toc":1719},[],{"title":25,"searchDepth":26,"depth":26,"links":1720},[],[1722,1731,1740,1749,1758],{"id":5,"title":6,"badge":7,"badges":1723,"buy_url":7,"complete_package_url":7,"description":15,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":18,"logo_wide":19,"meta":1726,"screenshot":28,"short_description":29,"short_title":7,"stem":30,"trial_url":31,"type":32,"url":33,"__hash__":34},[1724,1725],{"label":10,"color":11},{"label":13,"color":14},{"body":1727},{"type":22,"value":1728,"toc":1729},[],{"title":25,"searchDepth":26,"depth":26,"links":1730},[],{"id":36,"title":37,"badge":7,"badges":1732,"buy_url":7,"complete_package_url":7,"description":42,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":43,"logo_wide":44,"meta":1735,"screenshot":50,"short_description":51,"short_title":7,"stem":52,"trial_url":53,"type":32,"url":54,"__hash__":55},[1733,1734],{"label":40,"color":11},{"label":13,"color":14},{"body":1736},{"type":22,"value":1737,"toc":1738},[],{"title":25,"searchDepth":26,"depth":26,"links":1739},[],{"id":57,"title":58,"badge":59,"badges":1741,"buy_url":65,"complete_package_url":7,"description":66,"essentials_url":7,"extension":16,"external":17,"logo_component":7,"logo_square":67,"logo_wide":68,"meta":1744,"screenshot":74,"short_description":75,"short_title":7,"stem":76,"trial_url":7,"type":32,"url":77,"__hash__":78},[1742,1743],{"label":40,"color":11},{"label":63,"color":64},{"body":1745},{"type":22,"value":1746,"toc":1747},[],{"title":25,"searchDepth":26,"depth":26,"links":1748},[],{"id":80,"title":81,"badge":7,"badges":1750,"buy_url":89,"complete_package_url":89,"description":90,"essentials_url":91,"extension":16,"external":92,"logo_component":93,"logo_square":94,"logo_wide":7,"meta":1753,"screenshot":100,"short_description":101,"short_title":102,"stem":103,"trial_url":7,"type":104,"url":105,"__hash__":106},[1751,1752],{"label":84,"color":85},{"label":87,"color":88},{"body":1754},{"type":22,"value":1755,"toc":1756},[],{"title":25,"searchDepth":26,"depth":26,"links":1757},[],{"id":108,"title":109,"badge":7,"badges":1759,"buy_url":116,"complete_package_url":116,"description":117,"essentials_url":118,"extension":16,"external":92,"logo_component":119,"logo_square":120,"logo_wide":7,"meta":1763,"screenshot":126,"short_description":127,"short_title":128,"stem":129,"trial_url":7,"type":104,"url":130,"__hash__":131},[1760,1761,1762],{"label":84,"color":85},{"label":87,"color":88},{"label":114,"color":115},{"body":1764},{"type":22,"value":1765,"toc":1766},[],{"title":25,"searchDepth":26,"depth":26,"links":1767},[],[1769,1778,1787,1796,1805,1814],{"id":134,"title":135,"badge":7,"badges":1770,"description":143,"extension":16,"external":92,"github_slug":144,"logo_square":145,"logo_wide":146,"meta":1773,"repo_url":152,"screenshot":153,"short_description":154,"short_title":155,"stem":156,"type":157,"url":158,"__hash__":159},[1771,1772],{"label":138,"color":139},{"label":141,"color":142},{"body":1774},{"type":22,"value":1775,"toc":1776},[],{"title":25,"searchDepth":26,"depth":26,"links":1777},[],{"id":161,"title":162,"badge":7,"badges":1779,"description":167,"extension":16,"external":92,"github_slug":168,"logo_square":67,"logo_wide":169,"meta":1782,"repo_url":175,"screenshot":176,"short_description":177,"short_title":7,"stem":178,"type":179,"url":180,"__hash__":181},[1780,1781],{"label":138,"color":139},{"label":166,"color":14},{"body":1783},{"type":22,"value":1784,"toc":1785},[],{"title":25,"searchDepth":26,"depth":26,"links":1786},[],{"id":183,"title":184,"badge":7,"badges":1788,"description":189,"extension":16,"external":92,"github_slug":190,"logo_square":191,"logo_wide":192,"meta":1791,"repo_url":198,"screenshot":199,"short_description":200,"short_title":7,"stem":201,"type":202,"url":203,"__hash__":204},[1789,1790],{"label":138,"color":139},{"label":188,"color":85},{"body":1792},{"type":22,"value":1793,"toc":1794},[],{"title":25,"searchDepth":26,"depth":26,"links":1795},[],{"id":206,"title":207,"badge":7,"badges":1797,"description":213,"extension":16,"external":92,"github_slug":214,"logo_square":215,"logo_wide":216,"meta":1800,"repo_url":222,"screenshot":223,"short_description":224,"short_title":7,"stem":225,"type":226,"url":227,"__hash__":228},[1798,1799],{"label":138,"color":139},{"label":211,"color":212},{"body":1801},{"type":22,"value":1802,"toc":1803},[],{"title":25,"searchDepth":26,"depth":26,"links":1804},[],{"id":230,"title":231,"badge":7,"badges":1806,"description":236,"extension":16,"external":92,"github_slug":237,"logo_square":238,"logo_wide":239,"meta":1809,"repo_url":245,"screenshot":246,"short_description":247,"short_title":7,"stem":248,"type":226,"url":249,"__hash__":250},[1807,1808],{"label":138,"color":139},{"label":235,"color":14},{"body":1810},{"type":22,"value":1811,"toc":1812},[],{"title":25,"searchDepth":26,"depth":26,"links":1813},[],{"id":252,"title":253,"badge":7,"badges":1815,"description":257,"extension":16,"external":17,"github_slug":258,"logo_square":259,"logo_wide":7,"meta":1818,"repo_url":265,"screenshot":7,"short_description":266,"short_title":267,"stem":268,"type":157,"url":265,"__hash__":269},[1816,1817],{"label":138,"color":139},{"label":141,"color":142},{"body":1819},{"type":22,"value":1820,"toc":1821},[],{"title":25,"searchDepth":26,"depth":26,"links":1822},[],[1824,1833,1842,1851,1860,1869],{"id":134,"title":135,"badge":7,"badges":1825,"description":143,"extension":16,"external":92,"github_slug":144,"logo_square":145,"logo_wide":146,"meta":1828,"repo_url":152,"screenshot":153,"short_description":154,"short_title":155,"stem":156,"type":157,"url":158,"__hash__":159},[1826,1827],{"label":138,"color":139},{"label":141,"color":142},{"body":1829},{"type":22,"value":1830,"toc":1831},[],{"title":25,"searchDepth":26,"depth":26,"links":1832},[],{"id":161,"title":162,"badge":7,"badges":1834,"description":167,"extension":16,"external":92,"github_slug":168,"logo_square":67,"logo_wide":169,"meta":1837,"repo_url":175,"screenshot":176,"short_description":177,"short_title":7,"stem":178,"type":179,"url":180,"__hash__":181},[1835,1836],{"label":138,"color":139},{"label":166,"color":14},{"body":1838},{"type":22,"value":1839,"toc":1840},[],{"title":25,"searchDepth":26,"depth":26,"links":1841},[],{"id":183,"title":184,"badge":7,"badges":1843,"description":189,"extension":16,"external":92,"github_slug":190,"logo_square":191,"logo_wide":192,"meta":1846,"repo_url":198,"screenshot":199,"short_description":200,"short_title":7,"stem":201,"type":202,"url":203,"__hash__":204},[1844,1845],{"label":138,"color":139},{"label":188,"color":85},{"body":1847},{"type":22,"value":1848,"toc":1849},[],{"title":25,"searchDepth":26,"depth":26,"links":1850},[],{"id":206,"title":207,"badge":7,"badges":1852,"description":213,"extension":16,"external":92,"github_slug":214,"logo_square":215,"logo_wide":216,"meta":1855,"repo_url":222,"screenshot":223,"short_description":224,"short_title":7,"stem":225,"type":226,"url":227,"__hash__":228},[1853,1854],{"label":138,"color":139},{"label":211,"color":212},{"body":1856},{"type":22,"value":1857,"toc":1858},[],{"title":25,"searchDepth":26,"depth":26,"links":1859},[],{"id":230,"title":231,"badge":7,"badges":1861,"description":236,"extension":16,"external":92,"github_slug":237,"logo_square":238,"logo_wide":239,"meta":1864,"repo_url":245,"screenshot":246,"short_description":247,"short_title":7,"stem":248,"type":226,"url":249,"__hash__":250},[1862,1863],{"label":138,"color":139},{"label":235,"color":14},{"body":1865},{"type":22,"value":1866,"toc":1867},[],{"title":25,"searchDepth":26,"depth":26,"links":1868},[],{"id":252,"title":253,"badge":7,"badges":1870,"description":257,"extension":16,"external":17,"github_slug":258,"logo_square":259,"logo_wide":7,"meta":1873,"repo_url":265,"screenshot":7,"short_description":266,"short_title":267,"stem":268,"type":157,"url":265,"__hash__":269},[1871,1872],{"label":138,"color":139},{"label":141,"color":142},{"body":1874},{"type":22,"value":1875,"toc":1876},[],{"title":25,"searchDepth":26,"depth":26,"links":1877},[],1775771774222]