<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DERICK TEMFACK</title>
    <description>The latest articles on DEV Community by DERICK TEMFACK (@tderick).</description>
    <link>https://dev.to/tderick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F640362%2Ffc8f94a8-113e-46a8-8d57-6ecfece9fbd7.JPG</url>
      <title>DEV Community: DERICK TEMFACK</title>
      <link>https://dev.to/tderick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tderick"/>
    <language>en</language>
    <item>
      <title>From ChatGPT to Gemini: How We Built a GDPR-Compliant CV Parser for Odoo</title>
      <dc:creator>DERICK TEMFACK</dc:creator>
      <pubDate>Sun, 11 Jan 2026 23:10:17 +0000</pubDate>
      <link>https://dev.to/tderick/from-chatgpt-to-gemini-how-we-built-a-gdpr-compliant-cv-parser-for-odoo-nne</link>
      <guid>https://dev.to/tderick/from-chatgpt-to-gemini-how-we-built-a-gdpr-compliant-cv-parser-for-odoo-nne</guid>
      <description>&lt;p&gt;A few years ago, a business owner in France contacted us to help digitalize their workflow. This company specializes in recruiting people for &lt;strong&gt;Work-Study Programs&lt;/strong&gt; (known in France as &lt;a href="https://www.alternance.emploi.gouv.fr/je-suis-alternant" rel="noopener noreferrer"&gt;&lt;em&gt;Alternance&lt;/em&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In short, &lt;strong&gt;Alternance&lt;/strong&gt; is a system where you split your time between school and a job. It is a great deal because the student gets a degree for free, gains real work experience, and receives a salary based on the French minimum wage (SMIC). It is designed to help young people and job seekers enter the workforce or switch careers.&lt;/p&gt;

&lt;p&gt;The company acts as an expert middleman because the process can be overwhelming to manage alone. Here is how they operate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Finding Candidates:&lt;/strong&gt; They source students or job seekers interested in work-study programs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screening:&lt;/strong&gt; They interview applicants to ensure they meet official government criteria (age, educational background, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching with Employers:&lt;/strong&gt; They find companies looking for apprentices and send them the best profiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling Paperwork:&lt;/strong&gt; Once a company decides to hire, the agency handles the complex administrative steps and ensures contracts are signed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Basically, they bridge the gap between the apprentice and the employer, making the journey smoother for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck: Manual Data Entry
&lt;/h2&gt;

&lt;p&gt;Before contacting us, they were doing everything manually using notebooks and spreadsheets. We built a complete recruitment pipeline for them based on the &lt;strong&gt;&lt;a href="https://www.odoo.com/app/recruitment" rel="noopener noreferrer"&gt;Odoo&lt;/a&gt; Community&lt;/strong&gt; Recruitment module.&lt;/p&gt;

&lt;p&gt;We developed custom modules to extend Odoo's base functionality to support their specific workflow. Once completed, the solution allowed the company to track everything in one place: the database of applicants and companies, the recruitment stages, appointments, automated Mail/SMS notifications, the calendar, and digital contract signatures.&lt;/p&gt;

&lt;p&gt;The solution was a success. Everyone liked the new system, and it made their daily lives much simpler. However, one major pain point remained: &lt;strong&gt;Data Entry.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recruiters still had to find profiles and manually enter them into the recruitment pipeline before they could start working. The business owner was frustrated. He was paying recruiters to hunt for talent, but they were spending hours doing manual data entry.&lt;/p&gt;

&lt;p&gt;We decided to automate this using AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Automation Workflow
&lt;/h2&gt;

&lt;p&gt;Our first idea was to build an API so third parties could send data directly to the system. While this would have been ideal, the third-party sources were not ready for it.&lt;/p&gt;

&lt;p&gt;We reached an agreement to receive all applicant CVs via email instead. These were simple emails with the CV attached. Once we confirmed we were receiving the CVs in a dedicated mailbox, we designed the following automation workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read Emails:&lt;/strong&gt; Log in to the email provider and fetch unread emails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract Attachment:&lt;/strong&gt; Download the attached CV (usually a PDF).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCR Processing:&lt;/strong&gt; Perform Optical Character Recognition to extract raw text from the CV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Parsing:&lt;/strong&gt; Use an LLM to transform that raw text into a structured JSON format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Odoo Integration:&lt;/strong&gt; Use Odoo XML-RPC to create a record in the recruitment module. We also attach the original PDF so the recruiter has a reference if the AI misses anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assignment:&lt;/strong&gt; Automatically assign the lead to an available recruiter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb612rwb4hpkm73xdbzbs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb612rwb4hpkm73xdbzbs.png" alt="Generic Architecture of the System" width="545" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Iteration 1: Tesseract and ChatGPT
&lt;/h2&gt;

&lt;p&gt;For our first solution, we developed a microservice using &lt;strong&gt;&lt;a href="https://github.com/tesseract-ocr/tesseract" rel="noopener noreferrer"&gt;Tesseract&lt;/a&gt;&lt;/strong&gt; for OCR and the &lt;strong&gt;&lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;ChatGPT API&lt;/a&gt;&lt;/strong&gt; for the LLM logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9cts1f3p7ctjyugdnhy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9cts1f3p7ctjyugdnhy.png" alt="Concrete Architecture with Tesseract OCR and ChatGPT API" width="553" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worked fine for a couple of months, and the client was happy. However, technical issues started to appear. &lt;strong&gt;Tesseract&lt;/strong&gt; struggled to process certain CV layouts, especially scanned documents. Additionally, Tesseract was consuming too much RAM on our VPS and wasn't delivering the high level of accuracy we needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Iteration 2: LlamaParse for Better OCR
&lt;/h2&gt;

&lt;p&gt;To solve the weaknesses of Tesseract, we replaced it with a cloud service called &lt;strong&gt;&lt;a href="https://www.llamaindex.ai/llamaparse" rel="noopener noreferrer"&gt;LlamaParse&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5akx5yuo90ryu6cbcar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5akx5yuo90ryu6cbcar.png" alt="Concrete Architecture with LLamaParse OCR and ChatGPT API" width="582" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For context, &lt;strong&gt;LlamaParse&lt;/strong&gt; is a specialized tool created by &lt;a href="https://www.llamaindex.ai/" rel="noopener noreferrer"&gt;LlamaIndex&lt;/a&gt;. It is designed specifically to "read" and parse complex documents (like PDFs) so AI models can understand them better.&lt;/p&gt;

&lt;p&gt;Switching to LlamaParse was a great decision. It allowed us to process any CV format—scanned or digital—and obtain excellent results from the LLM. We ran this setup for a few months until we hit a new problem. This time, it wasn't technical; it was financial. &lt;strong&gt;ChatGPT was becoming too expensive.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Iteration 3: The DeepSeek Experiment
&lt;/h2&gt;

&lt;p&gt;My employer started noticing that we were burning too much money on the ChatGPT API. At the same time, &lt;strong&gt;&lt;a href="https://api-docs.deepseek.com/" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/strong&gt; was the new arrival in town, taking the internet by storm.&lt;/p&gt;

&lt;p&gt;We evaluated DeepSeek for a while. It was actually very good. We migrated from ChatGPT to DeepSeek, and the results were immediate: the budget that lasted one week on ChatGPT now lasted a whole month on DeepSeek, with even better results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdbjkeq0ctqflppa8mcn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdbjkeq0ctqflppa8mcn.png" alt="Concrete Architecture with LLamaParse OCR and DeepSeek API" width="587" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, we used DeepSeek for only two months before hitting a legal wall: &lt;strong&gt;&lt;a href="https://gdpr-info.eu/" rel="noopener noreferrer"&gt;GDPR Compliance&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DeepSeek was not compliant with European data protection laws (GDPR/RGPD). Since we were processing the personal data of French citizens, we simply could not continue using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Winning Setup: Google Gemini
&lt;/h2&gt;

&lt;p&gt;We conducted a new migration, this time to &lt;strong&gt;&lt;a href="https://ai.google.dev/gemini-api/docs" rel="noopener noreferrer"&gt;Google Gemini&lt;/a&gt;&lt;/strong&gt;, looking for a balance of affordability, efficiency, and compliance.&lt;/p&gt;

&lt;p&gt;The result was better than expected. Gemini is so capable that we were able to &lt;strong&gt;drop LlamaParse entirely&lt;/strong&gt;. We now use Gemini for both the Vision/OCR part and the data extraction. The process is very simple: we send the prompt along with the PDF attachment directly to Gemini.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruhnz3ex4jeisre07rkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruhnz3ex4jeisre07rkn.png" alt="Concrete Architecture with Gemini API for both OCR and JSON Output" width="493" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We haven't touched the code for this microservice in over six months because it just works. Everything runs smoothly, and we are fully compliant with GDPR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This journey taught us that software development is about iteration. We started with a basic open-source OCR, moved to specialized parsing tools, optimized for cost, and finally settled on a solution that offered the best mix of performance and compliance.&lt;/p&gt;

&lt;p&gt;Today, the client's recruiters no longer waste time on data entry. They focus on what they do best: finding the right job for the right student.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to hide username and password fields in the WordPress login form</title>
      <dc:creator>DERICK TEMFACK</dc:creator>
      <pubDate>Sat, 20 Aug 2022 12:42:00 +0000</pubDate>
      <link>https://dev.to/tderick/how-to-hide-username-and-password-fields-in-the-wordpress-login-form-4b1e</link>
      <guid>https://dev.to/tderick/how-to-hide-username-and-password-fields-in-the-wordpress-login-form-4b1e</guid>
      <description>&lt;p&gt;Sometimes on your WordPress site, you don’t want to have the default login form anymore. You might want your users to log in with social login or an external identity provider. In that case, you need to remove the &lt;strong&gt;username&lt;/strong&gt; and &lt;strong&gt;password&lt;/strong&gt; fields.&lt;/p&gt;

&lt;p&gt;To achieve that, we’ll use CSS to hide those fields. We’ll only work in the &lt;strong&gt;functions.php&lt;/strong&gt; file through the WordPress admin dashboard. So, you don’t need to be a specialist in WordPress to accomplish this manipulation. Our version of WordPress in this tutorial is 6.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Log in to your WordPress admin dashboard
&lt;/h2&gt;

&lt;p&gt;After logging in to your WordPress dashboard, navigate to &lt;strong&gt;Tools&amp;gt;Theme File Editor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzxl8n9xctwq6q8dl7h4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzxl8n9xctwq6q8dl7h4.png" alt="Open Theme File Editor" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Open the functions.php file
&lt;/h2&gt;

&lt;p&gt;When you are on the theme editor page, localize and open the &lt;strong&gt;functions.php&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vfba9dwxzahtjzzffxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vfba9dwxzahtjzzffxr.png" alt="functions.php file" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Insert the code and save
&lt;/h2&gt;

&lt;p&gt;Finally, we’ll insert the following piece of code at the end of the &lt;strong&gt;functions.php&lt;/strong&gt; file and save it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action( 'login_head', 'wpse_121687_hide_login' );
function wpse_121687_hide_login() {
    $style = '';
     $style .= '&amp;lt;style type="text/css"&amp;gt;';
     $style .= '.login form .user-pass-wrap{ display: none }';
     $style .= '.login form .submit { display: none }';
     $style .= '.login form .forgetmenot { display: none }';
     $style .= '.login form .input { display: none }';
     $style .= '.login form label { display: none }';
     $style .= '.login  #nav { display: none }';
     $style .= '.login  #mo_saml_button b { display: none }';
    $style .= '#login {margin-top: 150px}';
    $style .= '&amp;lt;/style&amp;gt;';

     echo $style; 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bsd1hm0otbeigr17mfd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bsd1hm0otbeigr17mfd.png" alt="Insert code in the functions.php file" width="749" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And voila, we have the desired result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9lsh18ub2kfspm0cfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9lsh18ub2kfspm0cfa.png" alt="Final result" width="749" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The purpose of this tutorial was to show you how to hide the username and password fields from the WordPress login form in a proper way. Everyone has his reasons to hide them. If you have encountered a problem applying this tutorial, don’t hesitate to leave a comment. It would be a pleasure for me to help you.&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>How to deploy Owncloud on Linux Server with docker</title>
      <dc:creator>DERICK TEMFACK</dc:creator>
      <pubDate>Tue, 17 May 2022 15:53:22 +0000</pubDate>
      <link>https://dev.to/tderick/how-to-deploy-owncloud-on-linux-server-with-docker-2bn7</link>
      <guid>https://dev.to/tderick/how-to-deploy-owncloud-on-linux-server-with-docker-2bn7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Owncloud&lt;/strong&gt; is a set of collaboratory tools that allow users to store their files, share them and easily collaborate on the same data. It’s an open-source project, so you can download and deploy it on your server without paying anything. Owncloud is a very popular files sharing solution and is extremely used with around 200 million users around the world.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmahtf62esdwlvcpc71r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmahtf62esdwlvcpc71r.png" alt="Owncloud, a file sharing solution" width="768" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Owncloud has a rich ecosystem and is accessible through various platforms:&lt;strong&gt;web, desktop, android, and iOS&lt;/strong&gt;. It is a suitable solution for all kinds of people, from individual people to big companies with solely the aim to increase productivity. It allows you to build your cloud infrastructure like &lt;a href="https://www.dropbox.com/" rel="noopener noreferrer"&gt;dropbox&lt;/a&gt; to gain your data sovereignty from &lt;a href="https://www.pcmag.com/picks/the-best-cloud-storage-and-file-sharing-services" rel="noopener noreferrer"&gt;big tech companies&lt;/a&gt; offering cloud storage and file sharing services. You can deploy Owncloud to your server or choose &lt;a href="https://owncloud.online/?ref=owncloud.com" rel="noopener noreferrer"&gt;Owncloud Saas&lt;/a&gt; to avoid caring about infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxchrzf3fw66dzf6zegn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxchrzf3fw66dzf6zegn.png" alt="Owncloud in mobile, web, and desktop" width="517" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is an Owncloud package for all major Linux distributions such as Debian, Ubuntu, Fedora, RedHat Enterprise Linux, CentOS, and OpenSUSE. There is now a docker version of the Owncloud server provided and maintained by Owncloud Inc, the company behind Owncloud. For me, the easy way to deploy it in production is to use docker. So, in this blog post, we will deploy it with docker and perhaps we’ll cover another deployment method in other posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisite for the deployment of Owncloud
&lt;/h2&gt;

&lt;p&gt;As we choose to deploy Owncloud in production using docker and docker-compose, there are some prerequisites before starting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux Server with SSH and root access&lt;/li&gt;
&lt;li&gt;Docker and docker-compose installed on that server&lt;/li&gt;
&lt;li&gt;A domain name pointed to that server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are too many Linux distributions out there, and everybody should ensure to properly install docker and docker-compose for his Linux distribution. After that, the remaining steps will remain the same for all of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment of Owncloud
&lt;/h2&gt;

&lt;p&gt;For Owncloud deployment with docker, we only need two files: &lt;strong&gt;a docker-compose file and a .env file&lt;/strong&gt;. The docker-compose file will allow us to compose the Owncloud server container, MariaDB database, and Redis cached server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OWNCLOUD_VERSION=latest
OWNCLOUD_DOMAIN=cloud.example.com
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
HTTP_PORT=8080
LETSENCRYPT_EMAIL=example@gmail.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.env file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"

volumes:
  files:
    driver: local
  mysql:
    driver: local
  redis:
    driver: local

services:
  owncloud:
    image: owncloud/server:${OWNCLOUD_VERSION}
    container_name: owncloud_server
    restart: always
    depends_on:
      - mariadb
      - redis
    environment:
      - OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
      - OWNCLOUD_DB_TYPE=mysql
      - OWNCLOUD_DB_NAME=owncloud
      - OWNCLOUD_DB_USERNAME=owncloud
      - OWNCLOUD_DB_PASSWORD=owncloud
      - OWNCLOUD_DB_HOST=mariadb
      - OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
      - OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - OWNCLOUD_MYSQL_UTF8MB4=true
      - OWNCLOUD_REDIS_ENABLED=true
      - OWNCLOUD_REDIS_HOST=redis
      - VIRTUAL_HOST=${OWNCLOUD_DOMAIN}
      - LETSENCRYPT_HOST=${OWNCLOUD_DOMAIN}
      - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
      - VIRTUAL_PORT=${HTTP_PORT}
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - files:/mnt/data

  mariadb:
    image: mariadb:10.5
    container_name: owncloud_mariadb
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=owncloud
      - MYSQL_USER=owncloud
      - MYSQL_PASSWORD=owncloud
      - MYSQL_DATABASE=owncloud
    command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=owncloud"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - mysql:/var/lib/mysql

  redis:
    image: redis:6
    container_name: owncloud_redis
    restart: always
    command: ["--databases", "1"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - redis:/data


#Use this configuration in production with nginx-proxy container
networks:
  default:
      name: nginx-proxy
      external: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;docker-compose.yml file&lt;/p&gt;

&lt;p&gt;Those files are well configured for a production environment. First, log in to your Linux server with SSH and type the following command to create the directory for Owncloud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir owncloud &amp;amp;&amp;amp; cd owncloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a file called &lt;strong&gt;docker-compose.yml&lt;/strong&gt;, copy the content shown in the above docker-compose.yml file and paste it into. Do the same for the &lt;strong&gt;.env&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano docker-compose.yml
nano .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: if you are not familiar with &lt;strong&gt;nano editor&lt;/strong&gt;, just type &lt;strong&gt;CTRL+SHIFT+V&lt;/strong&gt; to paste content, &lt;strong&gt;CTRL+o&lt;/strong&gt; to save and &lt;strong&gt;CTRL+x&lt;/strong&gt; to quit. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Explanation of docker-compose file
&lt;/h2&gt;

&lt;p&gt;Our docker-compose file is written following the docker compose version 3.9 specification. We have three volumes, three services, and one external network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Owncloud service
&lt;/h3&gt;

&lt;p&gt;It is the Owncloud container with the server code written in the &lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go programming language&lt;/a&gt; responsible for all the work performed by Owncloud. We use the latest version available in &lt;a href="https://hub.docker.com/r/owncloud/server" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt; as we specified through the environment variable &lt;strong&gt;OWNCLOUD_VERSION&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This service depends on mariadb service and redis service means that it starts solely when mariadb and redis services start. We have many environments variable and we’ll just explain a couple of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OWNCLOUD_DB_TYPE=mysql&lt;/strong&gt; specify the database connector. Owncloud support multiple database engines for production. We have Oracle 11 and 12, PostgreSQL 9, 10, 11, 12, 13 or 14 and MySQL 8+ or MariaDB 10.2, 10.3, 10.4 or 10.5 which is recommended. We use MariaDB 10.5 as recommended by the Owncloud official documentation in this blog post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OWNCLOUD_DB_HOST=mariadb&lt;/strong&gt; is the name of the service in the docker-compose file containing the database. In our file, the service is named &lt;strong&gt;mariadb&lt;/strong&gt; and we put it here to let the Owncloud server know where to find the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}&lt;/strong&gt; and &lt;strong&gt;OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}&lt;/strong&gt; set the admin username (which can be an email address) and admin password. We keep those values in the &lt;strong&gt;.env file&lt;/strong&gt; to keep them secure because Owncloud used them to create the admin user of our server and those credentials are used to login and manage the server. Make your password secure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VIRTUAL_HOST=${OWNCLOUD_DOMAIN}&lt;/strong&gt; is to tell our reverse proxy server to drive all traffic for &lt;strong&gt;OWNCLOUD_DOMAIN&lt;/strong&gt; to this container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LETSENCRYPT_HOST=${OWNCLOUD_DOMAIN}&lt;/strong&gt; and &lt;strong&gt;LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}&lt;/strong&gt; are used by &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let’s Encrypt&lt;/a&gt; to issue a free SSL certificate for our domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VIRTUAL_PORT=${HTTP_PORT}&lt;/strong&gt; is to tell the reverse proxy server the port used by the container for the incoming traffic. This value can be ignored in case the default port is &lt;strong&gt;80&lt;/strong&gt;. Owncloud used port &lt;strong&gt;8080&lt;/strong&gt;, it’s why we have set this value.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  mariadb service
&lt;/h3&gt;

&lt;p&gt;In the mariadb service, we use four environment variables where MYSQL_USER must be the same as OWNCLOUD_DB_USERNAME; MYSQL_PASSWORD the same as OWNCLOUD_DB_PASSWORD, and finally MYSQL_DATABASE the same as OWNCLOUD_DB_NAME. Those variables must be the same to ensure Owncloud is capable to connect to the MariaDB server.&lt;/p&gt;

&lt;h3&gt;
  
  
  redis service
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; is an excellent modern memory cache solution to use for distributed caching. It can be used for single and multi-server ownCloud installations, which provide file locking and can be set up in local or distributed environments. It’s used to improve the performance of an Owncloud server thanks to its faster access.&lt;/p&gt;

&lt;h3&gt;
  
  
  External network
&lt;/h3&gt;

&lt;p&gt;We want our installation to be protected by an SSL certificate, so we connect our containers to an external network connected to a reverse proxy server. Also, this way of deployment allows us to have multiple applications on the same server with docker and protect all of them with a free SSL certificate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation of .env file
&lt;/h2&gt;

&lt;p&gt;In the .env file, you must change OWNCLOUD_DOMAIN with your domain, ADMIN_USERNAME with the username or email you want to login to the server, ADMIN_PASSWORD with a strong password, and LETSENCRYPT_EMAIL with the email address to be used by let’s encrypt to issue the SSL certificate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start our cloud server
&lt;/h2&gt;

&lt;p&gt;We have explained to you all the configurations in the docker-compose file. These configurations are perfect for the production environment and you can easily adjust them according to your needs.&lt;/p&gt;

&lt;p&gt;The external network we declare in the docker-compose file doesn’t exist yet. Let’s create it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start your Owncloud instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j9mfwm8zrb312onn3vh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j9mfwm8zrb312onn3vh.png" alt="Owncloud status" width="794" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Owncloud instance is now running but is not accessible from the internet. We will now configure Nginx-proxy to drive traffic to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation of Nginx-proxy and acme-compagnon
&lt;/h2&gt;

&lt;p&gt;Nginx proxy is a container running Nginx and docker-gen which is a service that generates reverse proxy configs for Nginx and reloads Nginx when containers are started or stopped.&lt;/p&gt;

&lt;p&gt;This container is mounted on a docker socket to capture all events created by docker to be able to proxied any container with an env variable &lt;strong&gt;VIRTUAL_HOST&lt;/strong&gt; define. All containers that want to be proxied by Nginx-proxy must be connected to the same network with it. To know more about Nginx-proxy, visit the &lt;a href="https://github.com/nginx-proxy/nginx-proxy" rel="noopener noreferrer"&gt;GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ACME-compagnon is a compagnon for Nginx-proxy responsible to automate the creation, renewal, and use of SSL certificates for proxied Docker containers through ACME protocol. For more information about acme-compagnon, visit the &lt;a href="https://github.com/nginx-proxy/acme-companion" rel="noopener noreferrer"&gt;GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a ready-to-use template for Nginx-proxy in &lt;a href="https://gitlab.com/tderick/nginx-proxy-conf" rel="noopener noreferrer"&gt;my Gitlab repository&lt;/a&gt;. You just need to clone and run it. This configuration is well-suited for a production environment. Just type the following command in your terminal to make it run in less than a minute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
git clone https://gitlab.com/tderick/nginx-proxy-conf.git 
docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your Owncloud instance is running, and you can access it via your domain name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwai7wz6f3f6no0k51ora.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwai7wz6f3f6no0k51ora.png" alt="Owncloud login page" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After logging in with our credentials, we arrive at the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf10et1l3lahhum7z20d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf10et1l3lahhum7z20d.png" alt="Welcome message of Owncloud" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xkoe042c3ekvq0cfy0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xkoe042c3ekvq0cfy0y.png" alt="Files view of Owncloud" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we have explained to you how to deploy Owncloud in production in your Linux server with docker-compose and secure it with a free SSL certificate issued by Let’s Encrypt. Another thing you can do now is to deploy a &lt;a href="https://temfack.com/keycloak-sso-on-linux-server-with-docker-and-ssl/" rel="noopener noreferrer"&gt;keycloak SSO&lt;/a&gt; solution next to this to centralize authentication among all your application. It’s pretty easy at this step and doesn’t affect the previous installation. If you are instead a considerable fan of NextCloud, check my other blog post about the &lt;a href="https://temfack.com/nextcloud-in-your-linux-server-with-docker-and-ssl/" rel="noopener noreferrer"&gt;deployment of Nextcloud on a Linux server with docker&lt;/a&gt;. If you have any questions, leave a comment.&lt;/p&gt;

&lt;p&gt;If you like this tutorial, you can &lt;a href="https://www.buymeacoffee.com/tderick" rel="noopener noreferrer"&gt;buy me coffee&lt;/a&gt;. In the upcoming tutorial, we will explore Owncloud and discover what it allows us to do, and also explain how to connect Owncloud and keycloak SSO.&lt;/p&gt;

</description>
      <category>nextcloud</category>
      <category>owncloud</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to deploy NextCloud in your Linux Server with docker and SSL</title>
      <dc:creator>DERICK TEMFACK</dc:creator>
      <pubDate>Sat, 14 May 2022 10:51:19 +0000</pubDate>
      <link>https://dev.to/tderick/how-to-deploy-nextcloud-in-your-linux-server-with-docker-and-ssl-198k</link>
      <guid>https://dev.to/tderick/how-to-deploy-nextcloud-in-your-linux-server-with-docker-and-ssl-198k</guid>
      <description>&lt;p&gt;When you create your account and store your documents, images, etc in Google drive or dropbox, you are not the master of your data. Generally, we say that we use Google or Microsoft one drive for free but it’s not free. We pay for those free spaces with the personal data they collected. It’s where NextCloud comes in. In simple words, Nextcloud is your cloud infrastructure under your control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is NextCloud ?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NextCloud&lt;/strong&gt; is open-source software that allows you to run your personnel cloud service like dropbox. It gives you access to all your files wherever you are. It allows you to share and collaborate on documents, send and receive email, manage your calendar and have video chats. You can install the Nextcloud server software free on your Linux server and the client’s software on your Windows, OS X, or Linux machine, Android, and IOS mobile phone.&lt;/p&gt;

&lt;p&gt;The main drawback here is you need to pay for your Linux server to your VPS provider and you will be responsible for your server maintenance unless your choose Nextcloud Enterprise which comes with the support. For example, if you choose the Contabo provider, you can have your fully functional cloud solution with 200GB SSD, 8GO of RAM, and 4vCPU for only $6.99 a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisite before the installation of Nextcloud
&lt;/h2&gt;

&lt;p&gt;As we will deploy our NextCloud instance with docker, you need to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux Server with SSH and root access&lt;/li&gt;
&lt;li&gt;Docker and docker-compose installed on that server&lt;/li&gt;
&lt;li&gt;A domain name pointed to that server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment of NextCloud
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSTGRES_PASSWORD=yourdbstrongpassword
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;db.env file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"

services:
  nextcloud_db:
    image: postgres:alpine
    restart: always
    volumes:
      - nextcloud_dbdata:/var/lib/postgresql/data
    env_file:
      - db.env

  redis:
    image: redis:alpine
    restart: always

  nextcloud_web:
    image: nextcloud:apache
    restart: always
    volumes:
      - nextcloud:/var/www/html
    environment:
      - VIRTUAL_HOST=cloud.yourdomain.com
      - LETSENCRYPT_HOST=cloud.yourdomain.com
      - LETSENCRYPT_EMAIL=yourmail # &amp;lt;===== For let's encrypt
      - POSTGRES_HOST=nextcloud_db
      - REDIS_HOST=redis
    env_file:
      - db.env
    depends_on:
      - nextcloud_db
      - redis

  cron:
    image: nextcloud:apache
    restart: always
    volumes:
      - nextcloud:/var/www/html
    entrypoint: /cron.sh
    depends_on:
      - nextcloud_db
      - redis

volumes:
  nextcloud_dbdata:
  nextcloud:

#Use this configuration in production with nginx-proxy container
networks:
  default:
    external:
      name: nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;docker-compose.yml file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Login to your Linux server and type the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir nextcloud &amp;amp;&amp;amp; cd nextcloud
nano db.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copie the content of the &lt;strong&gt;db.env&lt;/strong&gt; in the above GitHub gist and paste it into the newly created file. After this, create a &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file and copy the content of the docker-compose.yml in the above Github gist and paste it into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Don’t forget the change the environment variables &lt;strong&gt;VIRTUAL_HOST&lt;/strong&gt; and &lt;strong&gt;LETSENCRYPT_HOST&lt;/strong&gt; with your domain name and &lt;strong&gt;LETSENCRYPT_EMAIL&lt;/strong&gt; with your email address.&lt;/p&gt;

&lt;p&gt;Now, it’s time to create the docker network that would be used to drive secure traffic to our Nextcloud instance through our domain name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let’s start our Nextcloud instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuoe5s4itvkbbgcrujtir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuoe5s4itvkbbgcrujtir.png" alt="docker-compose ps" width="681" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Nextcloud instance is now running but is not accessible from the internet. We will now configure Nginx-proxy to drive traffic to our Nextcloud instance. We will explain our docker-compose file after making our instance fully functional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation of Nginx-proxy and acme-compagnon
&lt;/h2&gt;

&lt;p&gt;Nginx proxy is a container running Nginx and docker-gen which is a service that generates reverse proxy configs for Nginx and reloads Nginx when containers are started or stopped.&lt;/p&gt;

&lt;p&gt;This container is mounted on a docker socket to capture all events created by docker to be able to proxied any container with an env variable &lt;strong&gt;VIRTUAL_HOST&lt;/strong&gt; define. All containers that want to be proxied by Nginx-proxy must be connected to the same network with it. To know more about Nginx-proxy, &lt;a href="https://github.com/nginx-proxy/nginx-proxy" rel="noopener noreferrer"&gt;visit the GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ACME-compagnon is a compagnon for Nginx-proxy responsible to automate the creation, renewal, and use of SSL certificates for proxied Docker containers through ACME protocol. For more information about acme-compagnon, &lt;a href="https://github.com/nginx-proxy/acme-companion" rel="noopener noreferrer"&gt;visit the GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a ready-to-use template for Nginx-proxy in my repository. You just need to clone and run it. I also use this template in all my projects. With this configuration, it’s easy to make things work in less than a minute. Just use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
git clone https://gitlab.com/tderick/nginx-proxy-conf.git 
docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your Nextcloud instance is running, and you can access it via your domain name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0jkkfeg0dfwtu4e2dxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0jkkfeg0dfwtu4e2dxc.png" alt="NextCloud first page" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you install Nextcloud, it doesn’t come with an admin account by default. You need to create it. Just fill in the form on the first page and hit the &lt;strong&gt;install&lt;/strong&gt; button. Don’t put email as username. if you do, you have the following error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxlbvz8bvbge9gwpis89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxlbvz8bvbge9gwpis89.png" alt="Error page when we use email as username" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After putting in a username and password, we will arrive at the following page listing the recommended apps to install in our instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysxhlkdnrrenmibilw55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysxhlkdnrrenmibilw55.png" alt="Recommend app to be installed in our nextcloud instance" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see there are applications for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calendar&lt;/li&gt;
&lt;li&gt;Contacts&lt;/li&gt;
&lt;li&gt;Mail&lt;/li&gt;
&lt;li&gt;Online edition and collaboration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will install other applications later. Just hit the &lt;strong&gt;install recommended apps&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1bxj8gs648pf3n73umz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1bxj8gs648pf3n73umz.png" alt="Nextcloud Dashboard" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our nextcloud instance is now installed and ready to use. Now, we can explain our docker-compose file to understand the magic behind this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker-compose file explanation
&lt;/h2&gt;

&lt;p&gt;In this docker-compose file, we use version 3.9 and we expose three services, two volumes, and one default external network.&lt;/p&gt;

&lt;h2&gt;
  
  
  nextcloud_db service
&lt;/h2&gt;

&lt;p&gt;Nextcloud support multiple DBMS: MySQL, MariaDB, Oracle, PostgresSQL. It’s up to you to choose your favorite DBMS. We choose to use Postgres as it is a very powerful solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  redis service
&lt;/h2&gt;

&lt;p&gt;Redis is an excellent modern memory cache solution to use for distributed caching. It’s used by Nextcloud to significantly improve the Nextcloud server performance with memory caching where frequently-requested objects are stored for faster retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  nextcloud_web service
&lt;/h2&gt;

&lt;p&gt;It’s the official Nextcloud container with all the features offered.&lt;/p&gt;

&lt;h2&gt;
  
  
  cron service
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://fr.wikipedia.org/wiki/Cron" rel="noopener noreferrer"&gt;Cron&lt;/a&gt; is a simple time-based job scheduler that runs small tasks on its own without the intervention of the user or the administrator. Cron is also an important part for Nextcloud to be running efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we explain to you how to deploy your Nextcloud instance in your Linux server with docker-compose and secure it with a free SSL certificate issued by Let’s Encrypt. Another thing you can do now is to deploy a &lt;a href="https://temfack.com/keycloak-sso-on-linux-server-with-docker-and-ssl/" rel="noopener noreferrer"&gt;keycloak SSO &lt;/a&gt;solution next to this to centralize authentication among all your application. It’s pretty easy at this step and doesn’t affect the previous installation. If you are instead a considerable fan of Owncloud, check my other blog post  about &lt;a href="https://temfack.com/how-to-deploy-owncloud-on-linux-server-with-docker/" rel="noopener noreferrer"&gt;the deployment of OwnCloud on a Linux server with docker&lt;/a&gt;.  If you have any questions, leave a comment.&lt;/p&gt;

&lt;p&gt;If you like this tutorial, you can &lt;a href="https://www.buymeacoffee.com/tderick" rel="noopener noreferrer"&gt;buy me coffee&lt;/a&gt;. In the upcoming tutorial, we will more explore Nextcloud.&lt;/p&gt;

</description>
      <category>nextcloud</category>
      <category>owncloud</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to deploy SSO (keycloak) on Linux server with docker-compose and SSL in less than 10min</title>
      <dc:creator>DERICK TEMFACK</dc:creator>
      <pubDate>Sun, 24 Apr 2022 16:21:12 +0000</pubDate>
      <link>https://dev.to/tderick/how-to-deploy-sso-keycloak-on-linux-server-with-docker-compose-and-ssl-in-less-than-10min-57aa</link>
      <guid>https://dev.to/tderick/how-to-deploy-sso-keycloak-on-linux-server-with-docker-compose-and-ssl-in-less-than-10min-57aa</guid>
      <description>&lt;p&gt;This article is also &lt;a href="https://temfack.com/how-to-deploy-sso-keycloak-on-linux-server-with-docker-compose-and-ssl-in-less-than-10min/" rel="noopener noreferrer"&gt;available on my blog&lt;/a&gt;. Check it out to support me. &lt;/p&gt;




&lt;p&gt;An SSO( Single Sign-On) is a system that allows users to access multiple services/platforms with the same credential. This system is very popular today and is used by the giants of the internet (Google, Facebook, etc). There are many platforms that sell this type of service among &lt;a href="https://www.onelogin.com/" rel="noopener noreferrer"&gt;onelogin&lt;/a&gt;, &lt;a href="https://www.okta.com/" rel="noopener noreferrer"&gt;okta&lt;/a&gt;, etc. In this story, we’ll set up our own SSO servers using keycloak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux Server with SSH Access&lt;/li&gt;
&lt;li&gt;Docker and docker-compose installed on this server&lt;/li&gt;
&lt;li&gt;A domain name point to this server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What is KeyCloak?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KeyCloak&lt;/strong&gt; is an open-source Identity and Access Management that allows us to add authentication in our application and secure service with minimum effort. It is a community project sponsored by Red Hat. Red Hat use it in their own products and also distribute it to their client as &lt;a href="https://access.redhat.com/products/red-hat-single-sign-on" rel="noopener noreferrer"&gt;Red Hat SSO&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;KeyCloak is a java project and is based on two popular protocols: &lt;strong&gt;SAML 2&lt;/strong&gt; and &lt;strong&gt;OpenID Connect&lt;/strong&gt;. With keycloak, you can easily enable social login or use an existing LDAP/Active Directory as a user federation.&lt;/p&gt;

&lt;p&gt;Keycloak is a very extensible tool and it gives you the possibility to customize it based on your need. It’s the best free SSO solution on the market and you can definitely choose it without being afraid if you want to set up your own SSO server. For further information, check their &lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;well-written documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation of KeyCloak&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have already prepared a &lt;strong&gt;docker-compose&lt;/strong&gt; file for you and it is the one that I use to deploy keycloak in my production environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"

services:
  postgres:
    image: postgres:13.2
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRESQL_DB}
      POSTGRES_USER: ${POSTGRESQL_USER}
      POSTGRES_PASSWORD: ${POSTGRESQL_PASS}
    volumes:
      - postgres_data:/var/lib/postgresql/data

  keycloak:
    depends_on:
      - postgres
    container_name: keycloak
    environment:
      DB_VENDOR: postgres
      DB_ADDR: postgres
      DB_DATABASE: ${POSTGRESQL_DB}
      DB_USER: ${POSTGRESQL_USER}
      DB_PASSWORD: ${POSTGRESQL_PASS}
      VIRTUAL_HOST: ${VIRTUAL_HOST}
      LETSENCRYPT_HOST: ${LETSENCRYPT_HOST}
      LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
      VIRTUAL_PORT: "8080"
      HTTPS_METHOD: redirect
      PROXY_ADDRESS_FORWARDING: "true" # &amp;lt;==== very important if you use reverse proxy
    image: jboss/keycloak:${KEYCLOAK_VERSION}
    restart: unless-stopped

volumes:
  postgres_data:

#Use this configuration in production with nginx-proxy container
networks:
  default:
    external:
      name: nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;docker-compose.yml file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEYCLOAK_VERSION=16.1.1
PORT_KEYCLOAK=8080
POSTGRESQL_USER=keycloak
POSTGRESQL_PASS=keycloak
POSTGRESQL_DB=keycloak
VIRTUAL_HOST=example.com
LETSENCRYPT_HOST=example.com
LETSENCRYPT_EMAIL=example@gmail.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.env file&lt;/p&gt;

&lt;p&gt;Our docker-compose file exposes two services, one volume, and one external network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Postgres service&lt;/strong&gt;&lt;br&gt;
Keycloak supports many databases like Postgres, MySQL/MariaDB, Oracle, etc. We use in our case Postgres.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keycloak service&lt;/strong&gt;&lt;br&gt;
This service depends on Postgres service. This means that the Postgres service will start or stop before the keycloak service. Keycloak has many different images in the Docker hub and we will use the one provided by the JBoss community. As we use Nginx-proxy as a reverse proxy and acme-compagnon for Let’s Encrypt certificate, we need to add three special environment variables in the keycloak service.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VIRTUAL_HOST&lt;/strong&gt; indicate the domain name (or subdomain name) that keycloak will be accessible through (VIRTUAL_HOST=auth.example.com)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LETSENCRYPT_HOST&lt;/strong&gt; contains the same value as VIRTUAL_HOST and indicates to acme-compagnon to ask for SSL certificate for this domain (or subdomain ) to Let’s encrypt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LETSENCRYPT_EMAIL&lt;/strong&gt; contains a valid email address required by let’s encrypt servers to issue an SSL certificate for our domain and to send notifications when the SSL certificate will be expired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PROXY_ADDRESS_FORWARDING&lt;/strong&gt; is set to true when we run keycloak behind a reverse proxy. Without this variable set to true, we can’t log in to our keycloak server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;network&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We use the Nginx-proxy container as a reverse proxy and it required all containers to be connected to a unique network so that it can easily handle traffic to them.&lt;/p&gt;

&lt;p&gt;The .env file is the one that contains all environment variables we use in our docker-compose file. We place it in the same directory with the docker-compose file and it’s automatically discovered when we make a &lt;strong&gt;docker-compose up&lt;/strong&gt;. You can modify it according to your need. If you don’t want to copy and paste the content of those files, you can clone the project with all this configuration from &lt;a href="https://gitlab.com/tderick/keycloak-docker-compose-template-file-for-easy-deploy-on-vps-behind-nginx-proxy-container" rel="noopener noreferrer"&gt;my repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to copy and paste, let’s do it. First, create a directory in your Linux server for this project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir keycloak &amp;amp;&amp;amp; cd keycloak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, copy the content of my docker-compose file and paste it into the docker-compose file you create in this directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do the same with the .env file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start keycloak service&lt;/strong&gt;&lt;br&gt;
Before starting keycloak, we need to create the external network that we declare in our docker-compose file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we can run our container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When starting keycloak for the first time, there are no users in his database, we need to create an admin user to manage our keycloak instance. To do that, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec keycloak \
    /opt/jboss/keycloak/bin/add-user-keycloak.sh \
    -u admin \
    -p admin \
&amp;amp;&amp;amp; docker restart keycloak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will add a user in the keycloak database with username admin and password admin. Change the password to a more secure one. Now, our keycloak server is ready. We can now install Nginx-proxy to make it accessible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation of Nginx-proxy and acme-compagnon&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nginx proxy is a container running Nginx and docker-gen which is a service that generates reverse proxy configs for Nginx and reloads Nginx when containers are started or stopped.&lt;/p&gt;

&lt;p&gt;This container is mounted on a docker socket to capture all events created by docker to be able to proxied any container with an env variable &lt;strong&gt;VIRTUAL_HOST&lt;/strong&gt; define. All containers that want to be proxied by Nginx-proxy must be connected to the same network with it. To know more about Nginx-proxy, visit &lt;a href="https://github.com/nginx-proxy/nginx-proxy" rel="noopener noreferrer"&gt;the GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ACME-compagnon is a compagnon for Nginx-proxy responsible to automate the creation, renewal, and use of SSL certificates for proxied Docker containers through ACME protocol. For more information about acme-compagnon, visit &lt;a href="https://github.com/nginx-proxy/acme-companion" rel="noopener noreferrer"&gt;the GitHub of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Again, I also have a prepared project in my repository to use without further configuration. You just need to clone and run it. I also use this template in all my projects. With this configuration, it’s easy to make things work in less than a minute. Just use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
git clone https://gitlab.com/tderick/nginx-proxy-conf.git 
docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your domain name and you will see the following output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszq8scdew9ht7m3shwgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszq8scdew9ht7m3shwgz.png" alt="keycloak home page" width="700" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on “&lt;strong&gt;Administration console&lt;/strong&gt;” and log in with the credential we created early.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5qsxbpdvjf4eq5ysfwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5qsxbpdvjf4eq5ysfwm.png" alt="keycloak authentication form" width="700" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulation, you have deployed an SSO system in your own server using docker and protected it with a free SSL certificate issued by &lt;strong&gt;Let’s Encrypt&lt;/strong&gt;.If you encounter a problem during the process, leave a comment and I will help you.&lt;/p&gt;

&lt;p&gt;In the upcoming stories, we will explore more keycloak. If you like this content, you can &lt;a href="https://www.buymeacoffee.com/tderick" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nginxproxy</category>
      <category>sso</category>
      <category>keycloak</category>
    </item>
  </channel>
</rss>
