<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Blog Name</title>
  <subtitle>Blog subtitle</subtitle>
  <id>http://blog.url.com/blog</id>
  <link href="http://blog.url.com/blog"/>
  <link href="http://blog.url.com/blog/feed.xml" rel="self"/>
  <updated>2026-03-13T00:00:00+00:00</updated>
  <author>
    <name>Blog Author</name>
  </author>
  <entry>
    <title>Zero-Knowledge Architecture for Your Lab</title>
    <link rel="alternate" href="http://blog.url.com/blog/2026/03/13/designing-zero-knowledge-architecture/"/>
    <id>http://blog.url.com/blog/2026/03/13/designing-zero-knowledge-architecture/</id>
    <published>2026-03-13T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;Part 3 of the &amp;ldquo;Building Zero-Knowledge Biotech Infrastructure&amp;rdquo; series&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="/blog/2026/02/03/why-biotech-cant-trust-the-cloud/"&gt;Part 1&lt;/a&gt;, we explained why standard cloud encryption leaves biotech data exposed during processing. In &lt;a href="/blog/2026/03/01/how-aws-nitro-enclaves-protect-biotech-data/"&gt;Part 2&lt;/a&gt;, we showed how AWS Nitro Enclaves provide hardware-enforced isolation with cryptographic attestation.&lt;/p&gt;

&lt;p&gt;Now the practical question: how do you actually design a zero-knowledge confidential computing architecture for your biotech organization? The technology is available. The hard part is the architectural decisions that determine whether it actually protects your data or just adds complexity.&lt;/p&gt;

&lt;p&gt;This post covers the five decisions that matter most.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Terms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Terms used throughout this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attestation&lt;/strong&gt;: A cryptographic process that proves exactly what code is running inside an enclave. Before releasing a decryption key, the key management service verifies a signed attestation document from the hardware itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PCR values&lt;/strong&gt;: Platform Configuration Register values. Cryptographic hashes that measure the exact software stack inside an enclave: the image, the kernel, the application binary. If anything changes, the hash changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vsock&lt;/strong&gt;: The single local socket channel an enclave uses to communicate with its parent instance. There is no other network interface. All data in and out travels through this channel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KMS&lt;/strong&gt;: Key Management Service. A cloud-hosted service for creating and controlling encryption keys. In a zero-knowledge architecture, KMS key policies use attestation values to ensure only verified enclave code can trigger decryption.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h2&gt;Decision 1: Who Owns the Encryption Keys in Your Zero-Knowledge Architecture?&lt;/h2&gt;

&lt;p&gt;This is the single most important architectural choice. Everything else follows from it.&lt;/p&gt;

&lt;p&gt;There are three models:&lt;/p&gt;

&lt;h3&gt;Platform-Managed Keys&lt;/h3&gt;

&lt;p&gt;The processing platform generates and stores encryption keys. Your data is encrypted, but the platform operator retains control of the keys. On most platforms, a sufficiently privileged administrator (one with root or hypervisor-level access) can potentially inspect process memory where key material is loaded during decryption operations. They may not exercise that access, and most platforms have policies against it. But the capability exists in the infrastructure, and no cryptographic control prevents it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt; Encryption at rest, encryption in transit, and the operator&amp;rsquo;s promise not to look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you don&amp;rsquo;t get:&lt;/strong&gt; Independence from the operator. If the platform is compromised, subpoenaed, or acquired, your data can be decrypted by someone other than you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this is acceptable:&lt;/strong&gt; When the data isn&amp;rsquo;t sensitive enough to justify the operational overhead of customer-owned keys. De-identified datasets, published reference data, non-proprietary analyses.&lt;/p&gt;

&lt;h3&gt;Customer-Managed Keys (Same Account)&lt;/h3&gt;

&lt;p&gt;You create encryption keys in the platform&amp;rsquo;s cloud account, but only you can access them through IAM policies. The platform operator has administrative access to the account but commits not to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt; Logical separation of key access. Audit trails showing who accessed what.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you don&amp;rsquo;t get:&lt;/strong&gt; Mathematical guarantees. An account administrator can always modify IAM policies. Your security depends on the operator&amp;rsquo;s discipline, not cryptographic enforcement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this is acceptable:&lt;/strong&gt; When you trust the operator and need simpler onboarding. Internal platforms where the &amp;ldquo;operator&amp;rdquo; is your own DevOps team.&lt;/p&gt;

&lt;h3&gt;Customer-Owned Keys (Separate Account)&lt;/h3&gt;

&lt;p&gt;You create encryption keys in your own cloud account. The processing platform never has administrative access to your keys. Key access is gated by hardware attestation: the key will only decrypt data inside a verified, unmodified enclave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt; Cryptographic proof that only attested code can access your data. The platform operator cannot decrypt your data even if they wanted to, even if they&amp;rsquo;re compromised, even if they&amp;rsquo;re compelled by court order. They don&amp;rsquo;t have the keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you don&amp;rsquo;t get:&lt;/strong&gt; Simplicity. Customer-owned keys require cross-account IAM roles, attestation-based key management policies, and a coordination process when the enclave code updates. Your team needs to understand key policies, or work with someone who does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this is necessary:&lt;/strong&gt; When you&amp;rsquo;re processing data where unauthorized access has existential consequences. Patient genomics, clinical trial data, proprietary drug discovery pipelines, pre-publication research.&lt;/p&gt;

&lt;h3&gt;Our Recommendation&lt;/h3&gt;

&lt;p&gt;For biotech data that matters, customer-owned keys in a separate account is the only model that provides guarantees rather than promises. Every other model has an administrator somewhere who can bypass the controls.&lt;/p&gt;

&lt;p&gt;This is harder to set up. It creates operational friction when enclave code updates (because attestation hashes change and key policies need updating). But when a customer asks &amp;ldquo;can you access our data?&amp;rdquo; the answer is &amp;ldquo;no, and here&amp;rsquo;s the key policy that proves it.&amp;rdquo; That&amp;rsquo;s a different conversation than &amp;ldquo;no, we promise.&amp;rdquo;&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Decision 2: Where Are Your Trust Boundaries?&lt;/h2&gt;

&lt;p&gt;A trust boundary is the line between &amp;ldquo;we trust the code on this side&amp;rdquo; and &amp;ldquo;we don&amp;rsquo;t trust anything on that side.&amp;rdquo; In a zero-knowledge architecture, you need to draw these lines explicitly.&lt;/p&gt;

&lt;h3&gt;The Zero-Knowledge Architecture in Practice&lt;/h3&gt;

&lt;p&gt;The diagram below shows a complete end-to-end flow for a confidential computing biotech workload: from your machine, through your own key management account, into the processing infrastructure, and back.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────────────┐
│                            YOUR MACHINE                                     │
│                                                                             │
│  Client CLI                                                                 │
│  1. Encrypts file locally with a data key                                   │
│  2. Uploads ciphertext only (plaintext never leaves this machine)           │
└──────────────────────────────┬──────────────────────────────────────────────┘
                               │ ciphertext only
                               ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                          YOUR CLOUD ACCOUNT                                 │
│                                                                             │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │  KMS Key  (you own it and the platform has no administrative access) │   │
│  │                                                                      │   │
│  │  Policy: kms:Decrypt permitted ONLY IF the request carries a valid   │   │
│  │  attestation document with a matching PCR0 hash.                     │   │
│  │  All other callers (including platform operators) are denied.        │   │
│  └─────────────────────────────┬────────────────────────────────────────┘   │
│                                │ attestation-gated decryption               │
│                                │ cross-account IAM role, PCR0 condition     │
└────────────────────────────────┼────────────────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                       PROCESSING INFRASTRUCTURE                             │
│                                                                             │
│  ┌─────────────────────┐   vsock only   ┌───────────────────────────────┐   │
│  │   Parent Instance   │◄══════════════►│        Nitro Enclave          │   │
│  │   (UNTRUSTED)       │                │        (TRUSTED)              │   │
│  │                     │                │                               │   │
│  │ Pulls ciphertext    │                │ 1. Attests to your KMS key    │   │
│  │ from object storage │                │ 2. Receives decryption key    │   │
│  │                     │                │    re-encrypted for enclave   │   │
│  │ Proxies KMS calls   │                │    public key only            │   │
│  │ on behalf of        │                │ 3. Decrypts data in memory    │   │
│  │ enclave             │                │ 4. Runs analysis pipeline     │   │
│  │                     │                │ 5. Re-encrypts results        │   │
│  │ Writes encrypted    │                │ 6. Returns ciphertext         │   │
│  │ results to storage  │                │    via vsock                  │   │
│  │                     │                │                               │   │
│  │ Never sees          │                │  No network. No persistent    │   │
│  │ plaintext           │                │  storage. No SSH access.      │   │
│  └─────────────────────┘                │  No operator access.          │   │
│                                         └───────────────────────────────┘   │
│  Hypervisor enforces memory isolation at hardware level                     │
│  Platform operators cannot read enclave memory even with root access        │
└────────────────────────────────┬────────────────────────────────────────────┘
                                 │ ciphertext only
                                 ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                            YOUR MACHINE                                     │
│                                                                             │
│  Client CLI retrieves and decrypts results locally                          │
│  using your own cloud credentials. Plaintext never existed outside          │
│  the enclave or your machine.                                               │
└─────────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The trusted zone is minimal: the hardware, the attested enclave code, and your own encryption key. Everything else (the parent instance, the network, the platform operator&amp;rsquo;s application code, cloud provider staff) is untrusted by design. The architecture assumes those layers are compromised and still protects your data.&lt;/p&gt;

&lt;h3&gt;What This Means for Your Architecture&lt;/h3&gt;

&lt;p&gt;When you draw trust boundaries this way, several things change:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Networking becomes simpler.&lt;/strong&gt; The enclave has no network access. You don&amp;rsquo;t need complex VPN configurations, network segmentation, or bastion hosts to protect data during processing. The enclave&amp;rsquo;s network isolation is enforced by hardware, not firewall rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring changes.&lt;/strong&gt; You can observe metadata (job timing, status, resource usage) but not data content. This requires different operational patterns than traditional infrastructure where you can SSH in and inspect files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updates require coordination.&lt;/strong&gt; When the enclave code changes, the attestation hash changes. If your key policy references a specific attestation hash, you need to update the policy before the new code can access your data. This is a feature (you control when to trust new code), but it&amp;rsquo;s also operational overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incident response is different.&lt;/strong&gt; If the infrastructure outside the enclave is compromised, your data is still encrypted. You can tell your customers exactly what was and wasn&amp;rsquo;t exposed, backed by cryptographic proof rather than forensic analysis.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Decision 3: Which Confidential Computing Technology?&lt;/h2&gt;

&lt;p&gt;Not all confidential computing is equal. The three major approaches have different properties, and the right choice depends on your workload.&lt;/p&gt;

&lt;h3&gt;AWS Nitro Enclaves&lt;/h3&gt;

&lt;p&gt;A stripped-down VM carved from your EC2 instance. No network, no persistent storage, no interactive access. Communication via a single vsock channel. Attestation via the Nitro Hypervisor&amp;rsquo;s PCR measurements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Minimal attack surface. Full VM-level isolation. No dependency on CPU-specific features. Works with any code that runs in a Linux environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; No network inside the enclave (all data must be passed via vsock). Available only on AWS. Memory and CPU are carved from the parent instance, so you need to size the parent for the enclave&amp;rsquo;s needs plus your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Batch processing workloads with clear input/output boundaries. This maps directly to proteomics and genomics workflows: a DIA proteomics run produces a bounded set of raw instrument files (typically 1-5 GB each) that flow through a search pipeline and yield quantified protein or peptide results. The data arrives, gets processed, and leaves. No persistent enclave state is needed between steps. The same model applies to WGS variant calling pipelines, where FASTQ inputs are processed against a reference and results are written out before the enclave terminates.&lt;/p&gt;

&lt;h3&gt;Intel SGX&lt;/h3&gt;

&lt;p&gt;Application-level enclaves that protect specific code and data within a process. The CPU encrypts enclave memory, and only code running inside the enclave can access it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Granular protection at the application level. Smaller trusted computing base (TCB) than VM-level approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; Limited enclave memory (historically capped at 128–256 MB enclave page cache; expanded in recent generations per Intel SGX documentation). Requires application code changes to use the SGX SDK. Side-channel attacks have been demonstrated against SGX enclave memory (Kocher et al., &amp;ldquo;Spectre Attacks,&amp;rdquo; IEEE S&amp;amp;P 2019; Van Bulck et al., &amp;ldquo;Foreshadow,&amp;rdquo; USENIX Security 2018). Performance overhead for enclave transitions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Workloads where you need to protect specific secrets (keys, credentials) within a larger application. Less suited for processing large biotech datasets that don&amp;rsquo;t fit in enclave memory.&lt;/p&gt;

&lt;h3&gt;AMD SEV-SNP&lt;/h3&gt;

&lt;p&gt;Encrypts the entire VM&amp;rsquo;s memory using a key managed by the AMD Secure Processor. The hypervisor cannot read the VM&amp;rsquo;s memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Transparent to application code. No code changes needed. Protects the entire VM memory space. Available on Azure (Confidential VMs) and GCP (Confidential Computing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; The trust boundary is the entire VM, which has a larger attack surface than a minimal enclave. The VM has full network access, so data exfiltration through application-level bugs is still possible. Attestation model is less mature than Nitro&amp;rsquo;s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Lift-and-shift scenarios where you want memory encryption without rewriting code. Workloads that need network access during processing.&lt;/p&gt;

&lt;h3&gt;Comparison&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Intel SGX&lt;/th&gt;
&lt;th&gt;AMD SEV-SNP&lt;/th&gt;
&lt;th&gt;Nitro Enclaves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Isolation scope&lt;/td&gt;
&lt;td&gt;Application partition&lt;/td&gt;
&lt;td&gt;Full VM&lt;/td&gt;
&lt;td&gt;Full VM (isolated from parent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory limit&lt;/td&gt;
&lt;td&gt;128–512 MB enclave page cache&lt;/td&gt;
&lt;td&gt;Entire VM&lt;/td&gt;
&lt;td&gt;Allocated from parent (flexible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network access&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;None (vsock only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent storage&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Full disk&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance overhead&lt;/td&gt;
&lt;td&gt;High for large datasets (memory paging)&lt;/td&gt;
&lt;td&gt;2–5% (per AMD SEV-SNP performance benchmarks)&lt;/td&gt;
&lt;td&gt;Near-zero compute, vsock I/O overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attack surface&lt;/td&gt;
&lt;td&gt;Smallest (app-level)&lt;/td&gt;
&lt;td&gt;Medium (full VM)&lt;/td&gt;
&lt;td&gt;Small (no network, no disk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ease of migration&lt;/td&gt;
&lt;td&gt;Difficult (requires app partitioning)&lt;/td&gt;
&lt;td&gt;Easy (lift-and-shift VMs)&lt;/td&gt;
&lt;td&gt;Medium (containerize, architect for vsock)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;h3&gt;Our Take&lt;/h3&gt;

&lt;p&gt;For biotech batch processing (which covers most genomics, proteomics, and clinical workloads), Nitro Enclaves&amp;rsquo; restrictions are features. No network means no data exfiltration. No persistent storage means no data leakage between jobs. No interactive access means no operator snooping. The minimal attack surface is exactly what you want when the data is too sensitive for &amp;ldquo;trust me&amp;rdquo; security models.&lt;/p&gt;

&lt;p&gt;If your workload requires interactive analysis (Jupyter notebooks, real-time dashboards, exploratory data science), Nitro Enclaves are the wrong tool. AMD SEV-SNP provides memory encryption without the network restriction, though with a larger trust boundary.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Decision 4: What Are Your Actual Threat Scenarios?&lt;/h2&gt;

&lt;p&gt;Security architecture without a threat model is just expensive plumbing. Before adopting confidential computing, be specific about what you&amp;rsquo;re protecting against.&lt;/p&gt;

&lt;h3&gt;Threats That Confidential Computing Addresses&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privileged insider access.&lt;/strong&gt; Cloud provider employees, platform operators, or your own administrators with infrastructure access. Nitro Enclaves prevent memory inspection even with root access on the host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure compromise.&lt;/strong&gt; An attacker gains access to your cloud account, object storage, or databases. With customer-owned keys and attestation, encrypted data remains encrypted. The attacker gets ciphertext they can&amp;rsquo;t decrypt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compelled disclosure.&lt;/strong&gt; A court order or regulatory demand targeting the platform operator. If the operator doesn&amp;rsquo;t have the keys, they can&amp;rsquo;t comply with a decryption demand. They can produce ciphertext and metadata, but not plaintext.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supply chain attacks.&lt;/strong&gt; Compromised dependencies, malicious container images, or tampered build pipelines. Attestation detects code changes: if the enclave code is modified, the attestation hash changes, and the key management service refuses to release the key.&lt;/p&gt;

&lt;h3&gt;Threats That Confidential Computing Doesn&amp;rsquo;t Address&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Application logic bugs and Iago attacks.&lt;/strong&gt; Attestation proves that a specific, verified binary is running inside the enclave. It says nothing about whether that code is correct or resistant to manipulation. A class of attacks known as Iago attacks exploits this boundary directly: a compromised host manipulates the return values of system calls made by the enclave, causing the enclave application to behave against its own interests, without ever breaking hardware isolation. The enclave is running exactly the attested code. The attested code is being fed malicious inputs it doesn&amp;rsquo;t validate. For a genomics pipeline, this could mean corrupted input dimensions that produce silently wrong variant calls, with no cryptographic alarm raised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Malicious or vulnerable code in the enclave itself.&lt;/strong&gt; This is the logical complement to Iago attacks. Attestation proves &lt;em&gt;what&lt;/em&gt; code is running, not whether that code is trustworthy. If vulnerable or malicious code is packaged into the enclave image, it will be attested and run with full access to decrypted data. The hardware did its job. The code didn&amp;rsquo;t. This shifts the security boundary in a fundamental way: confidential computing doesn&amp;rsquo;t eliminate the need to trust the enclave code. It makes that code the &lt;em&gt;only&lt;/em&gt; thing you need to trust. Which means code provenance and verification become your new perimeter.&lt;/p&gt;

&lt;p&gt;A rigorous verification pipeline before any enclave image is trusted should include formal code audit, thorough code review, static code analysis (SAST), Docker image security scanning, and independent PCR0 hash verification against the published enclave image. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data at rest and in transit.&lt;/strong&gt; Confidential computing protects data during processing. You still need encryption at rest and in transit. These are complementary, not alternatives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authorized misuse.&lt;/strong&gt; A legitimate user with valid credentials who misuses their access. Confidential computing doesn&amp;rsquo;t solve authorization problems. If someone is authorized to submit jobs and receive results, they can process any data they have access to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side-channel attacks on hardware.&lt;/strong&gt; Academic research has demonstrated side-channel attacks against some confidential computing technologies, notably Intel SGX (Spectre, Foreshadow, and subsequent variants). Nitro Enclaves have a smaller attack surface, but no hardware is immune to all possible physical attacks. The practical question is whether an attack requires physical hardware access (very difficult for cloud infrastructure) or can be mounted remotely.&lt;/p&gt;

&lt;h3&gt;Right-Sizing Your Security&lt;/h3&gt;

&lt;p&gt;Not every dataset needs zero-knowledge processing. The overhead (operational complexity, update coordination, debugging limitations) is justified when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unauthorized access triggers legal and contractual exposure (consult your compliance team on applicable regulations and data processing obligations)&lt;/li&gt;
&lt;li&gt;The data has competitive value (proprietary sequences, drug candidates, unpublished research)&lt;/li&gt;
&lt;li&gt;Your customers require cryptographic proof of protection (pharma sponsors, clinical partners)&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t afford a breach investigation that runs for months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For published reference genomes, synthetic test data, or de-identified population statistics, standard cloud security with proper access controls is sufficient and much simpler.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Decision 5: How Will You Handle the Operational Trade-offs?&lt;/h2&gt;

&lt;p&gt;Adopting zero-knowledge architecture means accepting specific constraints. Plan for them before you commit.&lt;/p&gt;

&lt;h3&gt;You Can&amp;rsquo;t Debug by Inspecting Data&lt;/h3&gt;

&lt;p&gt;When a job fails inside an enclave, you can see error codes, log messages, resource utilization, and timing information. You cannot inspect the input data, intermediate results, or output. This changes how you troubleshoot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical approach:&lt;/strong&gt; Build comprehensive logging into your pipeline code. Log data shapes (dimensions, row counts, file sizes) rather than data content. Create synthetic test datasets that exercise the same code paths as real data, so you can reproduce issues outside the enclave.&lt;/p&gt;

&lt;h3&gt;Updates Require Coordination&lt;/h3&gt;

&lt;p&gt;Changing the enclave code changes the attestation hash. Every customer whose key policy references the old hash needs to update their policy before the new code can process their data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical approach:&lt;/strong&gt; Plan a transition window where both old and new enclave versions run simultaneously. Notify customers in advance. Provide tooling that makes the policy update a single command, not a manual console operation.&lt;/p&gt;

&lt;h3&gt;Resource Sizing Is Inflexible&lt;/h3&gt;

&lt;p&gt;Enclave resources (CPU, memory) are allocated at launch and can&amp;rsquo;t grow. If your pipeline needs more memory than declared, the job fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical approach:&lt;/strong&gt; Profile your workloads on representative data. In proteomics search pipelines (MSFragger, MaxQuant), peak memory during database search typically runs 2-3x the raw file size when searching against a full human proteome database: a 4 GB raw file will require 16-24 GB at peak, not 4 GB. WGS variant calling pipelines (BWA-MEM2, GATK HaplotypeCaller) against 30x coverage inputs commonly peak at 32-64 GB depending on the reference and ploidy. Size for your largest expected input with 25-50% headroom. Extreme outlier inputs may need a separate resource profile rather than sizing everything for the worst case.&lt;/p&gt;

&lt;h3&gt;Vendor Lock-in Is Real&lt;/h3&gt;

&lt;p&gt;Nitro Enclaves are AWS-specific. Intel SGX is Intel-specific. AMD SEV-SNP runs on AMD hardware. Your confidential computing choice ties you to a platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical approach:&lt;/strong&gt; Keep your pipeline code portable. Use standard tools (Nextflow, Snakemake, standard bioinformatics packages). The enclave is the execution environment; the pipeline logic should run anywhere. If you need to switch platforms, the migration cost should be the enclave wrapper, not the analysis code.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Putting Your Confidential Computing Architecture Together&lt;/h2&gt;

&lt;p&gt;Key ownership is the one decision you cannot patch later. Technology choice, threat model scoping, and operational trade-offs can all evolve as your architecture matures. Trust boundaries can be redrawn. But if the wrong party holds the encryption keys from the start, no amount of hardware isolation recovers from it. Get that decision right, and the rest of the architecture follows.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AWS Nitro Enclaves for Biotech Data</title>
    <link rel="alternate" href="http://blog.url.com/blog/2026/03/01/how-aws-nitro-enclaves-protect-biotech-data/"/>
    <id>http://blog.url.com/blog/2026/03/01/how-aws-nitro-enclaves-protect-biotech-data/</id>
    <published>2026-03-01T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;Part 2 in the &amp;ldquo;Building Zero-Knowledge Biotech Infrastructure&amp;rdquo; series&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="/blog/2026/02/03/why-biotech-cant-trust-the-cloud/"&gt;Part 1&lt;/a&gt;, we explained why standard cloud security fails for biotech: your data sits in plaintext memory during processing, and the cloud provider&amp;rsquo;s staff can access it. Encryption at rest and in transit protects data everywhere except where it matters most.&lt;/p&gt;

&lt;p&gt;This post covers the fix. AWS Nitro Enclaves create hardware-isolated virtual machines where your genomics pipeline, mass spec analysis, or clinical data processing runs in memory that the cloud provider cannot access. Not &amp;ldquo;won&amp;rsquo;t access per policy.&amp;rdquo; Cannot access, enforced by hardware.&lt;/p&gt;

&lt;p&gt;We chose Nitro Enclaves after evaluating Intel SGX, AMD SEV-SNP, and the confidential computing offerings from Azure and GCP. Here&amp;rsquo;s why, and how the architecture works in practice for life sciences workloads.&lt;/p&gt;

&lt;h2&gt;What Makes Nitro Enclaves Different&lt;/h2&gt;

&lt;p&gt;Most confidential computing approaches encrypt VM memory (AMD SEV) or create application-level enclaves (Intel SGX). AWS Nitro Enclaves take a different path: they carve out a completely isolated virtual machine from your EC2 instance, with no network access, no persistent storage, and no interactive login.&lt;/p&gt;

&lt;p&gt;That sounds restrictive. It is. And that&amp;rsquo;s the point.&lt;/p&gt;

&lt;p&gt;A Nitro Enclave is a stripped-down VM that runs inside your EC2 instance. When you launch it, the &lt;a href="https://aws.amazon.com/ec2/nitro/"&gt;Nitro Hypervisor&lt;/a&gt; allocates dedicated CPU cores and memory from the parent instance. Those resources become inaccessible to the parent. The hypervisor enforces this boundary in hardware. No software on the parent instance, no AWS operations engineer, no privileged process can read the enclave&amp;rsquo;s memory.&lt;/p&gt;

&lt;p&gt;The enclave communicates with the parent through exactly one channel: a local socket connection called vsock. That&amp;rsquo;s it. No TCP/IP networking. No disk. No SSH. This minimal attack surface is what makes the security model credible.&lt;/p&gt;

&lt;h3&gt;The Architecture in Practice&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
|                    EC2 Instance                         |
|                                                         |
|  ┌──────────────────┐    vsock    ┌──────────────────┐  |
|  |   Parent VM      |&amp;lt;===========&amp;gt;|   Nitro Enclave  |  |
|  |                  |             |                  |  |
|  | - Network access |             | - No network     |  |
|  | - Disk access    |             | - No disk        |  |
|  | - SSH access     |             | - No SSH         |  |
|  | - Proxy to S3,   |             | - Your pipeline  |  |
|  |   KMS, etc.      |             |   runs here      |  |
|  └──────────────────┘             └──────────────────┘  |
|                                                         |
|  Nitro Hypervisor (hardware-enforced isolation)         |
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The parent VM handles all I/O: pulling encrypted data from S3, proxying requests to AWS KMS for key management, and writing encrypted results back to storage. The enclave handles all computation on decrypted data. The two communicate over vsock.&lt;/p&gt;

&lt;p&gt;This separation means the parent never sees plaintext data. It handles only ciphertext. The enclave handles plaintext but has no way to exfiltrate it (no network, no disk). The only output path is vsock back to the parent, and your application controls what goes over that channel.&lt;/p&gt;

&lt;h2&gt;Cryptographic Attestation: Proving What Code Is Running&lt;/h2&gt;

&lt;p&gt;Hardware isolation is half the story. The other half is proving that the enclave is running exactly the code you expect.&lt;/p&gt;

&lt;p&gt;Every Nitro Enclave contains a &lt;a href="https://github.com/aws/aws-nitro-enclaves-nsm-api"&gt;Nitro Security Module&lt;/a&gt; (NSM), a hardware component that generates cryptographic attestation documents. When your enclave starts, the NSM measures everything about the environment: the enclave image, the kernel, the application code. These measurements become PCR (Platform Configuration Register) values, essentially cryptographic hashes of the exact software stack running inside the enclave.&lt;/p&gt;

&lt;p&gt;The attestation document is signed by the NSM using a key that chains back to the &lt;a href="https://docs.aws.amazon.com/enclaves/latest/user/verify-root.html"&gt;AWS Nitro Attestation Root CA&lt;/a&gt;. You can verify this chain independently. The document contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PCR0&lt;/strong&gt;: Hash of the enclave image (your entire application package)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PCR1&lt;/strong&gt;: Hash of the Linux kernel running inside the enclave&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PCR2&lt;/strong&gt;: Hash of the application binary&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PCR3&lt;/strong&gt;: Hash of the IAM role attached to the parent instance&lt;/li&gt;
&lt;li&gt;A public key generated inside the enclave (the private key never leaves)&lt;/li&gt;
&lt;li&gt;A timestamp and nonce for freshness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the foundation for zero-trust key management. Instead of trusting people or organizations, you trust code, and you verify that trust cryptographically.&lt;/p&gt;

&lt;h2&gt;Zero-Trust Key Management with KMS&lt;/h2&gt;

&lt;p&gt;Here&amp;rsquo;s where it gets practical for biotech workloads. AWS KMS supports &lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/services-nitro-enclaves.html"&gt;condition keys that reference attestation values&lt;/a&gt;. You can create a KMS key policy that says: &amp;ldquo;Only allow decryption when the request comes from an enclave with this exact PCR0 value.&amp;rdquo;&lt;/p&gt;

&lt;p&gt;The flow works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your genomics data sits encrypted in S3, protected by a KMS key&lt;/li&gt;
&lt;li&gt;The parent instance pulls the encrypted data and sends it to the enclave via vsock&lt;/li&gt;
&lt;li&gt;The enclave requests the decryption key from KMS, attaching its attestation document&lt;/li&gt;
&lt;li&gt;KMS validates the attestation: Is the signature valid? Does PCR0 match the policy?&lt;/li&gt;
&lt;li&gt;If valid, KMS decrypts the data key and re-encrypts it with the enclave&amp;rsquo;s public key (from the attestation document)&lt;/li&gt;
&lt;li&gt;The enclave decrypts using its private key, which was generated inside the enclave and has never existed anywhere else&lt;/li&gt;
&lt;li&gt;Your pipeline processes the data&lt;/li&gt;
&lt;li&gt;Results are encrypted with an output key before being sent back through vsock&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The critical detail: KMS re-encrypts the response using the enclave&amp;rsquo;s public key. The parent instance proxies the KMS request and response, but it only ever sees ciphertext. The plaintext data key exists only inside the enclave&amp;rsquo;s memory.&lt;/p&gt;

&lt;p&gt;If someone modifies the enclave image (changes the analysis code, adds a data exfiltration step, swaps in a different application), the PCR0 hash changes. The KMS policy rejects the request. The data stays encrypted.&lt;/p&gt;

&lt;p&gt;This is cryptographic proof, not a compliance checkbox. You can audit the enclave image, verify its hash matches the KMS policy, and know mathematically that only that specific code can access the decryption keys.&lt;/p&gt;

&lt;h2&gt;What This Means for Biotech Workloads&lt;/h2&gt;

&lt;h3&gt;Genomics: Variant Calling in an Enclave&lt;/h3&gt;

&lt;p&gt;A typical secure genomics pipeline using Nitro Enclaves (building on the &lt;a href="/blog/2025/02/01/setting-up-nf-core-with-microk8s/"&gt;Nextflow/nf-core patterns&lt;/a&gt; many teams already use):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3 (encrypted FASTQ/BAM)
    |
    v
Parent Instance (pulls encrypted files)
    |
    v [vsock - encrypted data]
Nitro Enclave:
    1. Gets decryption key from KMS (attested)
    2. Decrypts genomic data
    3. Runs alignment (BWA-MEM2) + variant calling (GATK)
    4. Encrypts VCF output with results key
    5. Sends encrypted results via vsock
    |
    v
Parent Instance (writes to S3)
    |
    v
S3 (encrypted VCF/results)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At no point does the parent instance see decrypted patient genomes. The alignment and variant calling happen entirely inside the enclave. The output VCF is encrypted before it leaves.&lt;/p&gt;

&lt;p&gt;For organizations processing patient-derived whole genome sequencing data, this architecture satisfies the security requirements that keep legal teams from approving cloud workloads. The data is protected during the processing step that traditional encryption misses.&lt;/p&gt;

&lt;h3&gt;Proteomics: Secure Mass Spec Analysis&lt;/h3&gt;

&lt;p&gt;Mass spectrometry raw files from drug discovery contain proprietary biomarker signatures worth years of R&amp;amp;D investment. The same enclave architecture protects this IP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raw mzXML/mzML files encrypted at rest in S3&lt;/li&gt;
&lt;li&gt;Decryption only inside an attested enclave&lt;/li&gt;
&lt;li&gt;Peptide identification and quantification run in isolated memory&lt;/li&gt;
&lt;li&gt;Results encrypted before leaving the enclave&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The performance characteristics matter here. Nitro Enclaves run on dedicated CPU cores carved from the parent instance. Compute-bound tasks like spectral matching and database searching run at near-native speed. The overhead comes from data transfer through vsock, which adds latency for the initial data load but doesn&amp;rsquo;t affect processing speed once data is in enclave memory.&lt;/p&gt;

&lt;h3&gt;Multi-Party Collaboration Without Trust&lt;/h3&gt;

&lt;p&gt;The most compelling use case for biotech is collaborative analysis where neither party trusts the other with raw data.&lt;/p&gt;

&lt;p&gt;Two pharmaceutical companies want to run a combined analysis on their patient cohorts. Neither wants to share raw data. With Nitro Enclaves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both companies agree on the analysis code and audit it&lt;/li&gt;
&lt;li&gt;Both set KMS policies restricting their keys to enclaves running that exact code (verified by PCR0)&lt;/li&gt;
&lt;li&gt;The enclave decrypts both datasets, runs the combined analysis&lt;/li&gt;
&lt;li&gt;Each company receives encrypted aggregate results, encrypted with their own output key&lt;/li&gt;
&lt;li&gt;Neither company&amp;rsquo;s raw data leaves the enclave. Neither company needs to trust the other, or the infrastructure operator&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern eliminates the &amp;ldquo;clean room&amp;rdquo; problem that blocks multi-site clinical collaborations and competitive pre-competitive research. The trust is in the code, verified by hardware, not in legal agreements between organizations.&lt;/p&gt;

&lt;h2&gt;How Nitro Enclaves Compare to Alternatives&lt;/h2&gt;

&lt;p&gt;We evaluated three confidential computing approaches before choosing Nitro Enclaves for biotech workloads:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Intel SGX&lt;/th&gt;
&lt;th&gt;AMD SEV-SNP&lt;/th&gt;
&lt;th&gt;AWS Nitro Enclaves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolation scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application partition&lt;/td&gt;
&lt;td&gt;Full VM&lt;/td&gt;
&lt;td&gt;Full VM (isolated from parent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;128-512 MB enclave page cache&lt;/td&gt;
&lt;td&gt;Entire VM&lt;/td&gt;
&lt;td&gt;Allocated from parent (flexible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;None (vsock only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Persistent storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Full disk&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High for large datasets (memory paging)&lt;/td&gt;
&lt;td&gt;2-5%&lt;/td&gt;
&lt;td&gt;Near-zero compute, vsock I/O overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attack surface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Smallest (app-level)&lt;/td&gt;
&lt;td&gt;Medium (full VM)&lt;/td&gt;
&lt;td&gt;Small (no network, no disk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ease of migration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Difficult (requires app partitioning)&lt;/td&gt;
&lt;td&gt;Easy (lift-and-shift VMs)&lt;/td&gt;
&lt;td&gt;Medium (containerize, architect for vsock)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Why SGX didn&amp;rsquo;t work for us:&lt;/strong&gt; The enclave page cache (EPC) is limited to 128-512 MB. Genomics and proteomics datasets routinely exceed this. When data spills beyond the EPC, performance degrades dramatically due to memory paging, sometimes by 10-100x. Intel has improved this with SGX2, but the fundamental memory constraint makes it impractical for large scientific workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why SEV-SNP is worth considering:&lt;/strong&gt; AMD&amp;rsquo;s approach encrypts the entire VM memory with minimal performance impact. It&amp;rsquo;s the easiest migration path: take your existing VM, enable SEV-SNP, and your memory is encrypted. The tradeoff is a larger attack surface. The VM still has networking, storage, and a full operating system. For organizations that need confidential computing with minimal re-architecture, it&amp;rsquo;s a strong option. Azure and GCP both offer SEV-SNP based confidential VMs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why we chose Nitro Enclaves:&lt;/strong&gt; For our proof-of-concept, we needed a technology where the security model is real, not a configuration promise that degrades the moment someone misconfigures an IAM policy or a network ACL. Nitro Enclaves deliver that. The isolation isn&amp;rsquo;t enforced by software rules that an admin can override — it&amp;rsquo;s enforced by the Nitro Hypervisor at the hardware level. There is no network interface to misconfigure because none exists. There is no disk to accidentally leave unencrypted because the enclave has no storage. And the KMS attestation policy doesn&amp;rsquo;t check whether you claim to be running approved code — it verifies the cryptographic hash of the actual binary in memory.
That distinction matters for a PoC. We wanted to demonstrate to biotech clients that confidential computing isn&amp;rsquo;t security theater layered on top of standard cloud infrastructure. It&amp;rsquo;s a fundamentally different trust model: the math proves what code is running, the hardware enforces what that code can access, and neither the cloud operator nor a compromised admin can circumvent it. The architectural work is real (you have to design for vsock communication), but for high-sensitivity biotech data, the security properties justify the engineering investment.&lt;/p&gt;

&lt;h2&gt;Practical Limitations You Should Know&lt;/h2&gt;

&lt;p&gt;Nitro Enclaves aren&amp;rsquo;t a drop-in solution. The constraints that make them secure also make them harder to work with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No persistent storage.&lt;/strong&gt; Everything in the enclave exists only in memory. If the enclave crashes, intermediate results are gone. Your application needs to stream results to the parent via vsock for durable storage. For long-running genomics pipelines, this means building checkpointing into your vsock communication protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No network access.&lt;/strong&gt; Every external service call (S3, KMS, databases, reference genome downloads) must go through the parent as a vsock proxy. AWS provides a &lt;a href="https://github.com/aws/aws-nitro-enclaves-cli"&gt;vsock proxy&lt;/a&gt; for KMS, but other services require custom proxy code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory constraints.&lt;/strong&gt; The enclave shares memory with the parent instance. A &lt;code&gt;r5.4xlarge&lt;/code&gt; (128 GiB) might allocate 96 GiB to the enclave, leaving 32 GiB for the parent proxy. For memory-intensive bioinformatics tools (BWA-MEM, STAR, large protein databases), you need to plan instance sizing carefully. There&amp;rsquo;s no swap. Out-of-memory kills the enclave process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging is painful.&lt;/strong&gt; You cannot SSH into the enclave. No interactive console, no debugger attachment. You get stdout/stderr through &lt;code&gt;nitro-cli console&lt;/code&gt;, but only in debug mode. And debug mode changes PCR values, which breaks attestation policies. The development workflow is: build outside the enclave, test inside with debug mode, then switch to production mode with updated KMS policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One enclave per instance.&lt;/strong&gt; Each EC2 instance supports a single enclave. You can&amp;rsquo;t run multiple isolated workloads on the same instance.&lt;/p&gt;

&lt;p&gt;These are real engineering challenges. We&amp;rsquo;ve built a vsock proxy layer that handles KMS, S3, and custom service endpoints through a unified API, along with a checkpointing system for long-running pipeline stages. That tooling took months to get right. Organizations should budget for the architectural work, especially the vsock communication layer and the proxy infrastructure for external services.&lt;/p&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;If your organization is evaluating confidential computing for genomics, proteomics, or clinical workloads, here&amp;rsquo;s a practical starting path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Identify your highest-sensitivity workload.&lt;/strong&gt; Don&amp;rsquo;t boil the ocean. Pick one pipeline that processes data your legal team won&amp;rsquo;t approve for standard cloud deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Containerize it.&lt;/strong&gt; Nitro Enclaves are built from Docker images. If your pipeline already runs in a container, you&amp;rsquo;re halfway there. If it doesn&amp;rsquo;t, &lt;a href="/blog/2025/01/11/devops-what-why-how/"&gt;containerization is the prerequisite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Prototype the vsock communication.&lt;/strong&gt; Build a simple proof-of-concept that sends data to the enclave and receives results. The &lt;a href="https://github.com/aws/aws-nitro-enclaves-sdk-c"&gt;AWS Nitro Enclaves SDK&lt;/a&gt; and the &lt;a href="https://github.com/aws/aws-nitro-enclaves-cli"&gt;vsock proxy&lt;/a&gt; are the starting points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Set up KMS attestation.&lt;/strong&gt; Create a KMS key with a policy that requires enclave attestation. This is where the zero-trust model clicks into place. Once you see your KMS policy rejecting requests from non-attested callers, the security model becomes concrete.&lt;/p&gt;

&lt;p&gt;These steps get you to a working prototype. But the engineering is only half the challenge. Before you commit to an architecture, you need to make harder decisions: who owns the encryption keys, where you draw trust boundaries, and how you&amp;rsquo;ll handle the operational overhead of attestation-based systems.&lt;/p&gt;

&lt;p&gt;In Part 3, we cover the five architectural decisions that determine whether confidential computing actually protects your data or just adds complexity.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Why Biotech Can't Trust the Cloud</title>
    <link rel="alternate" href="http://blog.url.com/blog/2026/02/03/why-biotech-cant-trust-the-cloud/"/>
    <id>http://blog.url.com/blog/2026/02/03/why-biotech-cant-trust-the-cloud/</id>
    <published>2026-02-03T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;Part 1 of 3 in the &amp;ldquo;Building Zero-Knowledge Biotech Infrastructure&amp;rdquo; series&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most biotech organizations can&amp;rsquo;t use cloud computing for their most valuable data. Not because the cloud isn&amp;rsquo;t technically capable (AWS could process 50TB of whole-genome sequencing data in days instead of months). Not because teams don&amp;rsquo;t want to (IT and bioinformatics groups know the operational benefits).&lt;/p&gt;

&lt;p&gt;They can&amp;rsquo;t use it because legal and compliance teams won&amp;rsquo;t sign off and they&amp;rsquo;re right to refuse.&lt;/p&gt;

&lt;p&gt;The standard cloud security model (encrypt data at rest, encrypt data in transit, trust the provider&amp;rsquo;s compliance certifications) doesn&amp;rsquo;t actually protect data during processing. When your variant calling pipeline runs, or your mass spec analysis executes, or your imaging segmentation processes, the data sits in plaintext memory on infrastructure the cloud provider controls. Their hypervisor can access it. Their operations staff can access it. Anyone who compromises those systems can access it.&lt;/p&gt;

&lt;p&gt;For genomics, proteomics, clinical trials, and manufacturing data, that&amp;rsquo;s not acceptable. Patient genomes are permanent identifiers. Biomarker signatures represent years of R&amp;amp;D investment. Manufacturing processes are core IP. Clinical trial data carries regulatory liability.&lt;/p&gt;

&lt;p&gt;This post explains why the current cloud trust model fails for biotech. In Part 2, we&amp;rsquo;ll show you how confidential computing changes the equation by making data processing cryptographically verifiable and hardware-isolated.&lt;/p&gt;

&lt;h2&gt;Why Biotech Data Is Different&lt;/h2&gt;

&lt;p&gt;Biotech data isn&amp;rsquo;t like other enterprise data. The sensitivity spans every &amp;ldquo;-omics&amp;rdquo; domain and imaging modality, but for different reasons. What unites them: you can&amp;rsquo;t afford a breach, you can&amp;rsquo;t undo exposure, and standard cloud security doesn&amp;rsquo;t address the actual threats.&lt;/p&gt;

&lt;h3&gt;Genomics: The Permanent Identifier&lt;/h3&gt;

&lt;p&gt;Genomic data is the ultimate personally identifiable information. Unlike passwords or credit cards, a genome can&amp;rsquo;t be changed or reissued after a breach.&lt;/p&gt;

&lt;p&gt;A leaked genome identifies you and your relatives, reveals disease predispositions, and remains compromised permanently. There&amp;rsquo;s no remediation path. The data exposure is irreversible.&lt;/p&gt;

&lt;p&gt;This isn&amp;rsquo;t theoretical. In October 2023, &lt;a href="https://www.sec.gov/Archives/edgar/data/1804591/000180459123000039/me-20231004.htm"&gt;23andMe disclosed&lt;/a&gt; that hackers accessed genetic data from 6.9 million users. The attackers used credential stuffing to breach individual accounts, then exploited the &amp;ldquo;DNA Relatives&amp;rdquo; feature to access data from millions of connected profiles. The stolen data (including ancestry information, birth years, and genetic heritage) appeared on hacking forums within weeks.&lt;/p&gt;

&lt;h3&gt;Proteomics: The Billion-Dollar Biomarker&lt;/h3&gt;

&lt;p&gt;Mass spectrometry data from proteomics studies often represents years of biomarker discovery work. A leaked proteomic signature for early cancer detection could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give competitors a multi-year head start&lt;/li&gt;
&lt;li&gt;Invalidate pending patents worth hundreds of millions&lt;/li&gt;
&lt;li&gt;Compromise ongoing clinical validation studies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike genomics, proteomics data changes over time, which makes longitudinal studies even more valuable and their protection more critical.&lt;/p&gt;

&lt;h3&gt;Imaging: Regulatory and Technical Challenges&lt;/h3&gt;

&lt;p&gt;Pathology slides, radiology images, and microscopy data present unique challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Patient re-identification&lt;/strong&gt;: AI can identify individuals from medical images even without metadata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diagnostic liability&lt;/strong&gt;: Leaked images could be used to challenge clinical decisions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GxP compliance&lt;/strong&gt;: Images used in drug submissions must maintain chain of custody&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single high-resolution pathology slide from a clinical trial can be 2-5 GB and contain enough information to identify the patient, the disease state, and potentially compromise the trial&amp;rsquo;s blinding.&lt;/p&gt;

&lt;h3&gt;Cell and Gene Therapy: Manufacturing Secrets&lt;/h3&gt;

&lt;p&gt;Cell therapy combines multiple sensitivity concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Patient-derived materials&lt;/strong&gt;: Autologous therapies use the patient&amp;rsquo;s own cells&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manufacturing processes&lt;/strong&gt;: Differentiation protocols and expansion conditions are core IP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch records&lt;/strong&gt;: Single-cell sequencing of manufacturing intermediates reveals process performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When your product literally contains patient cells, the line between patient data and manufacturing data disappears entirely.&lt;/p&gt;

&lt;h3&gt;Clinical Trials: The Regulatory Gauntlet&lt;/h3&gt;

&lt;p&gt;For research institutions and pharmaceutical companies, clinical data carries the highest stakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.fda.gov/regulatory-information/search-fda-guidance-documents/part-11-electronic-records-electronic-signatures-scope-and-application"&gt;21 CFR Part 11&lt;/a&gt;&lt;/strong&gt;: FDA requires audit trails and access controls that most cloud deployments can&amp;rsquo;t prove&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.hhs.gov/hipaa/for-professionals/security/index.html"&gt;HIPAA Security Rule&lt;/a&gt;&lt;/strong&gt;: Breach penalties up to $1.5 million per violation category&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.ich.org/page/efficacy-guidelines"&gt;ICH E6 (GCP)&lt;/a&gt;&lt;/strong&gt;: Clinical trial data must be attributable, legible, contemporaneous, original, and accurate, with documented evidence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EU Clinical Trials Regulation&lt;/strong&gt;: Requires specific technical measures for data protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The Regulatory Landscape Is Getting More Complex&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;&lt;a href="https://artificialintelligenceact.eu/"&gt;EU AI Act&lt;/a&gt;&lt;/strong&gt; includes provisions for high-risk AI systems in healthcare&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.npc.gov.cn/englishnpc/c23934/202104/d6d1f4bc5412448ab2f0914e38e53121.shtml"&gt;China&amp;rsquo;s biosecurity laws&lt;/a&gt;&lt;/strong&gt; restrict the export of human genetic data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State-level genetic privacy laws&lt;/strong&gt; are multiplying faster than compliance teams can track&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://gdpr-info.eu/art-9-gdpr/"&gt;GDPR Article 9&lt;/a&gt;&lt;/strong&gt; treats genetic data as a &amp;ldquo;special category&amp;rdquo; requiring explicit consent and additional safeguards&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brazil&amp;rsquo;s LGPD&lt;/strong&gt; and other emerging privacy regimes add more jurisdictional complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The Bottom Line: Every Domain Has Exposure&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;th&gt;Primary Risk&lt;/th&gt;
&lt;th&gt;Regulatory Pressure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Genomics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;WGS, WES, RNA-seq&lt;/td&gt;
&lt;td&gt;Patient re-identification, permanent PII&lt;/td&gt;
&lt;td&gt;HIPAA, GDPR, state genetic laws&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Proteomics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mass spec raw files, peptide IDs&lt;/td&gt;
&lt;td&gt;IP theft, biomarker leakage&lt;/td&gt;
&lt;td&gt;Trade secrets, patent risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metabolomics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LC-MS/MS profiles&lt;/td&gt;
&lt;td&gt;Diagnostic IP, patient health status&lt;/td&gt;
&lt;td&gt;HIPAA, IVD regulations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Imaging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pathology, radiology, microscopy&lt;/td&gt;
&lt;td&gt;Patient ID from images, diagnostic liability&lt;/td&gt;
&lt;td&gt;HIPAA, GxP for submissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cell Therapy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single-cell seq, batch records&lt;/td&gt;
&lt;td&gt;Manufacturing IP, patient-derived data&lt;/td&gt;
&lt;td&gt;21 CFR 1271, HIPAA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clinical Trials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EDC data, adverse events&lt;/td&gt;
&lt;td&gt;Trial integrity, patient safety&lt;/td&gt;
&lt;td&gt;21 CFR 11, ICH E6, GDPR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Drug Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compound libraries, screening data&lt;/td&gt;
&lt;td&gt;Competitive intelligence loss&lt;/td&gt;
&lt;td&gt;Trade secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Biologics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sequence data, expression systems&lt;/td&gt;
&lt;td&gt;Manufacturing process IP&lt;/td&gt;
&lt;td&gt;21 CFR 600s, biosimilar competition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;h2&gt;Current Cloud Security Solutions for Biotech (And Why They Fail)&lt;/h2&gt;

&lt;p&gt;Organizations facing these challenges typically choose from a menu of imperfect options:&lt;/p&gt;

&lt;h3&gt;Option 1: On-Premises Everything&lt;/h3&gt;

&lt;p&gt;The traditional approach: build your own data center, hire your own system administrators, maintain your own hardware refresh cycles.&lt;/p&gt;

&lt;p&gt;This can work, but the economics are brutal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single high-memory node for genome assembly costs $50,000+ and sits idle most of the time between analysis runs&lt;/li&gt;
&lt;li&gt;Recruiting bioinformatics talent is hard; finding people who can also manage HPC infrastructure is harder&lt;/li&gt;
&lt;li&gt;Hardware procurement cycles mean you&amp;rsquo;re perpetually behind on the latest capabilities&lt;/li&gt;
&lt;li&gt;Disaster recovery requires duplicating your entire infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For well-funded pharmaceutical companies, this works. For academic labs, startups, and smaller biotechs, the capital requirements are prohibitive.&lt;/p&gt;

&lt;h3&gt;Option 2: VPNs and Private Cloud&lt;/h3&gt;

&lt;p&gt;Connect your on-premises network to a cloud VPC via VPN. Run your workloads on dedicated instances. Treat the cloud as an extension of your data center.&lt;/p&gt;

&lt;p&gt;This doesn&amp;rsquo;t solve the trust problem. You&amp;rsquo;re still trusting the cloud provider completely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A compromised hypervisor or malicious insider can access memory on your &amp;ldquo;private&amp;rdquo; instances&lt;/li&gt;
&lt;li&gt;Your encryption keys are managed by the same provider who manages your compute. They can access both&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Private&amp;rdquo; subnets still run on shared physical infrastructure&lt;/li&gt;
&lt;li&gt;You have no visibility into who actually accessed your data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Business Associate Agreements (BAAs) that cloud providers offer for HIPAA workloads are legal protection, not technical protection. They define who pays the fine when something goes wrong. They don&amp;rsquo;t prevent things from going wrong.&lt;/p&gt;

&lt;h3&gt;Option 3: Encrypt Everything&lt;/h3&gt;

&lt;p&gt;The most common approach: encrypt data at rest in S3, encrypt data in transit with TLS, and call it a day.&lt;/p&gt;

&lt;p&gt;This protects against a narrow set of threats: someone stealing hard drives from the data center, or intercepting network traffic. It does nothing for the actual processing phase.&lt;/p&gt;

&lt;p&gt;When your &lt;a href="/blog/2025/02/01/setting-up-nf-core-with-microk8s/"&gt;genomics pipeline&lt;/a&gt; runs, the data must be decrypted. It exists in plaintext in memory on a virtual machine running on shared infrastructure. Any of the following can access it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud provider employees with privileged access&lt;/li&gt;
&lt;li&gt;Attackers who compromise the hypervisor&lt;/li&gt;
&lt;li&gt;Nation-state actors with lawful intercept capabilities&lt;/li&gt;
&lt;li&gt;Anyone who can execute a cold boot attack or memory dump&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encryption at rest and in transit protects data &lt;strong&gt;except when you&amp;rsquo;re actually using it&lt;/strong&gt;. That exception is where all the interesting attacks happen.&lt;/p&gt;

&lt;h3&gt;Encryption Comparison: Where Your Data Is Vulnerable&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Approach&lt;/th&gt;
&lt;th&gt;Data at Rest&lt;/th&gt;
&lt;th&gt;Data in Transit&lt;/th&gt;
&lt;th&gt;Data in Use (Processing)&lt;/th&gt;
&lt;th&gt;Protection Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard Cloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Encrypted (S3/EBS)&lt;/td&gt;
&lt;td&gt;✅ Encrypted (TLS)&lt;/td&gt;
&lt;td&gt;❌ Plaintext in memory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt; (Vulnerable during processing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Private VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;❌ Plaintext in memory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt; (Provider still has access)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customer-Managed Keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;❌ Plaintext in memory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MEDIUM&lt;/strong&gt; (Key and compute separated, but still vulnerable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confidential Computing (TEE)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;✅ Encrypted&lt;/td&gt;
&lt;td&gt;✅ Encrypted in enclave&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;HIGH&lt;/strong&gt; (Protected at all stages)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;h3&gt;Option 4: Compliance Certifications&lt;/h3&gt;

&lt;p&gt;&amp;ldquo;But AWS is SOC 2 Type II certified! They&amp;rsquo;re HIPAA eligible! They have FedRAMP authorization!&amp;rdquo;&lt;/p&gt;

&lt;p&gt;True. These certifications matter. They demonstrate that the cloud provider has security controls, follows processes, and submits to audits.&lt;/p&gt;

&lt;p&gt;What they can&amp;rsquo;t do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevent a malicious insider from accessing customer data&lt;/li&gt;
&lt;li&gt;Stop a sophisticated attacker who compromises privileged credentials&lt;/li&gt;
&lt;li&gt;Give you visibility into who actually accessed your data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certifications are table stakes for running regulated workloads. They&amp;rsquo;re not a guarantee of security.&lt;/p&gt;

&lt;h2&gt;Why Cloud Security Trust Models Don&amp;rsquo;t Work for Biotech&lt;/h2&gt;

&lt;p&gt;Cloud marketing materials talk about security features. They don&amp;rsquo;t mention the trust model underneath.&lt;/p&gt;

&lt;p&gt;When you use cloud computing, you&amp;rsquo;re trusting the provider&amp;rsquo;s security team configured things correctly. You&amp;rsquo;re trusting their employees won&amp;rsquo;t abuse privileged access. You&amp;rsquo;re trusting contractors were vetted appropriately. You&amp;rsquo;re trusting incident response will detect and disclose breaches. You&amp;rsquo;re trusting key management infrastructure won&amp;rsquo;t be compromised. You&amp;rsquo;re trusting legal teams won&amp;rsquo;t comply with overbroad government requests. You&amp;rsquo;re trusting executives won&amp;rsquo;t change policies later.&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s enormous trust to place in an organization you&amp;rsquo;ve never met, whose financial incentives don&amp;rsquo;t always align with your security requirements.&lt;/p&gt;

&lt;p&gt;Who can theoretically access your &amp;ldquo;encrypted&amp;rdquo; data on a major cloud provider? Cloud operations engineers with hypervisor access. Security team members investigating incidents on shared infrastructure. On-call personnel responding to alerts. Key management administrators who operate the HSMs backing your &amp;ldquo;customer-managed&amp;rdquo; keys. Government agencies with legal process (valid or otherwise). Auditors reviewing security controls. Third-party contractors on specialized projects. Anyone who compromises credentials for any of the above.&lt;/p&gt;

&lt;p&gt;This isn&amp;rsquo;t about cloud providers being bad actors. They employ talented security professionals and invest billions in infrastructure protection. The problem is architectural: centralized computing requires centralized trust, and that centralized trust becomes a single point of failure. No amount of perimeter security can fully address it.&lt;/p&gt;

&lt;h3&gt;The Trust Hierarchy in Traditional Cloud Computing&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│                  YOUR SENSITIVE DATA                    │
│         (genomics, proteomics, clinical trials)         │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
         ┌───────────────────────┐
         │   Hypervisor Layer    │ ◄─── Cloud ops engineers
         │  (Full memory access) │ ◄─── Security team
         └───────────┬───────────┘ ◄─── On-call staff
                     │             ◄─── Contractors
                     ▼             ◄─── Government orders
         ┌───────────────────────┐
         │  Key Management (HSM) │ ◄─── Key admins
         │ (Decryption possible) │ ◄─── Auditors
         └───────────────────────┘

              SINGLE POINT OF FAILURE
         Every layer can access your data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Traditional cloud architecture creates a trust hierarchy where your sensitive biotech data sits at the top, but every layer below can access it. The hypervisor layer has full memory access during processing. Cloud operations engineers, security teams, on-call staff, and contractors all have potential access paths. Key management systems that handle your encryption keys are operated by the same administrators. Government orders can compel access at any layer.&lt;/p&gt;

&lt;p&gt;This creates a single point of failure: the cloud provider&amp;rsquo;s access controls and employee vetting. Your data security depends entirely on trusting their processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With Confidential Computing (TEE):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│         ENCRYPTED ENCLAVE (Hardware-Isolated)           │
│         Your data + code in protected memory            │
│    ✓ Hypervisor CANNOT access                           │
│    ✓ Cloud provider CANNOT access                       │
│    ✓ Cryptographic attestation proves integrity         │
└─────────────────────────────────────────────────────────┘

              ZERO-TRUST ARCHITECTURE
         Hardware enforces isolation, not policies
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Confidential computing eliminates the trust hierarchy. Your data and processing code run inside a hardware-isolated encrypted enclave. The hypervisor cannot access it. The cloud provider cannot access it. Cryptographic attestation proves the integrity of the execution environment. The hardware enforces isolation, not policies or promises.&lt;/p&gt;

&lt;h2&gt;The Cloud Provider Insider Threat in Biotech&lt;/h2&gt;

&lt;p&gt;In enterprise security discussions, &amp;ldquo;insider threat&amp;rdquo; usually means employees at your organization going rogue. But for cloud computing, the more relevant insider threat is employees at the cloud provider.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://www.verizon.com/business/resources/reports/dbir/"&gt;2024 Verizon Data Breach Investigations Report&lt;/a&gt;, insider threats and privilege misuse account for a significant portion of breaches. These aren&amp;rsquo;t typically malicious. They&amp;rsquo;re usually mistakes, like misconfigured permissions or misdirected emails. But they happen.&lt;/p&gt;

&lt;p&gt;Now multiply that by the number of people with privileged access to cloud infrastructure. Major cloud providers employ thousands of operations staff with varying levels of access. Each one represents potential risk.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.theguardian.com/world/2013/jun/06/us-tech-giants-nsa-data"&gt;Edward Snowden disclosures&lt;/a&gt; revealed that intelligence agencies specifically targeted cloud provider infrastructure for mass surveillance. The PRISM program collected data directly from the servers of major tech companies. Whether through legal compulsion or technical compromise, government access to cloud data is a documented reality.&lt;/p&gt;

&lt;p&gt;For biotech specifically, this matters because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clinical trial data&lt;/strong&gt; could be valuable for investment decisions or competitor intelligence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drug discovery research&lt;/strong&gt; (including compound libraries, screening data, and hit-to-lead optimization) represents huge R&amp;amp;D investments worth stealing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Biomarker signatures&lt;/strong&gt; from proteomics or metabolomics studies could invalidate years of discovery work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manufacturing processes&lt;/strong&gt; for biologics and cell therapies are core IP that competitors would pay millions to access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Patient data&lt;/strong&gt; can be used for blackmail, identity theft, or discrimination&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Genetic data&lt;/strong&gt; has implications for individuals and their relatives for generations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proprietary algorithms&lt;/strong&gt; for image analysis or variant interpretation represent significant competitive advantages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &amp;ldquo;we&amp;rsquo;re not important enough to target&amp;rdquo; argument misses the point. Breaches don&amp;rsquo;t discriminate. You don&amp;rsquo;t need to be the intended target to have your data exposed when infrastructure is compromised.&lt;/p&gt;

&lt;h2&gt;What Secure Cloud Computing for Biotech Actually Requires&lt;/h2&gt;

&lt;p&gt;Given these challenges, what would a truly secure cloud model look like for biotech?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: process data without anyone seeing it.&lt;/strong&gt; Not &amp;ldquo;encrypted at rest and in transit&amp;rdquo; but encrypted during processing too. The compute infrastructure should be mathematically incapable of accessing plaintext data, even if every employee at the provider wanted to. This applies whether you&amp;rsquo;re running variant calling on whole-genome sequencing, peptide identification on mass spec raw files, image segmentation on pathology slides, or statistical analysis on clinical trial endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: prove mathematically that no one saw it.&lt;/strong&gt; Trust but verify isn&amp;rsquo;t enough. You need cryptographic proof that the code processing your data is exactly the code you approved, running in an environment that prevents data exfiltration. Not a compliance certification. Not a contractual promise. Mathematical certainty. For regulated environments, this proof becomes audit evidence. For IP-sensitive work, it becomes competitive protection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third: scale like cloud, secure like on-prem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The point of cloud computing is elasticity: spin up 1,000 cores for a day, pay for what you use, then spin them down. Security measures shouldn&amp;rsquo;t require giving that up. If your &amp;ldquo;secure&amp;rdquo; solution requires dedicated hardware with long procurement cycles, you&amp;rsquo;ve just reinvented on-premises computing with extra steps and higher costs. Proteomics core facilities need burst capacity for peak instrument output. Cell therapy manufacturers need to scale with production batches. Clinical trials need to process data from multiple sites simultaneously. &lt;a href="/blog/2025/01/11/devops-what-why-how/"&gt;Modern DevOps practices&lt;/a&gt; enable this elasticity, but only if the security model supports it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth: maintain regulatory compliance.&lt;/strong&gt; Whatever solution exists must satisfy HIPAA, GDPR, 21 CFR Part 11, SOC 2, GxP, ICH E6. Not by adding more legal agreements, but by providing technical controls that exceed regulatory requirements. The goal isn&amp;rsquo;t just passing an audit. It&amp;rsquo;s having technical measures so strong that the audit becomes a formality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fifth: work with existing tools.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scientists have workflows they know and trust:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Genomics&lt;/strong&gt;: &lt;a href="/blog/2025/02/01/setting-up-nf-core-with-microk8s/"&gt;Nextflow&lt;/a&gt;/Snakemake pipelines, BWA, GATK, STAR&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proteomics&lt;/strong&gt;: MaxQuant, Proteome Discoverer, custom R/Python scripts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Imaging&lt;/strong&gt;: CellProfiler, QuPath, deep learning models&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statistics&lt;/strong&gt;: R, SAS, specialized clinical trial analysis packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A secure platform that requires rewriting everything in a custom framework is dead on arrival. The tools must come to the data, not the other way around.&lt;/p&gt;

&lt;h2&gt;Confidential Computing: The Technology That Changes Biotech Cloud Security&lt;/h2&gt;

&lt;p&gt;These requirements sound impossible. For most of computing history, they were. But a category of technology called &lt;strong&gt;confidential computing&lt;/strong&gt; has emerged in the past few years that makes them achievable.&lt;/p&gt;

&lt;p&gt;The basic idea: what if the hardware itself enforced security boundaries? What if there were a region of memory that even the operating system (even the hypervisor) couldn&amp;rsquo;t access? What if code running in that region could prove cryptographically that it hadn&amp;rsquo;t been tampered with?&lt;/p&gt;

&lt;p&gt;Technologies like &lt;a href="https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/overview.html"&gt;Intel SGX&lt;/a&gt;, &lt;a href="https://www.amd.com/en/developer/sev.html"&gt;AMD SEV&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/ec2/nitro/nitro-enclaves/"&gt;AWS Nitro Enclaves&lt;/a&gt; provide exactly this. They&amp;rsquo;re called &lt;strong&gt;Trusted Execution Environments (TEEs)&lt;/strong&gt;, and they fundamentally change what&amp;rsquo;s possible for secure cloud computing.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="/blog/2026/03/01/how-aws-nitro-enclaves-protect-biotech-data/"&gt;next post&lt;/a&gt;, we&amp;rsquo;ll dive deep into how AWS Nitro Enclaves work, why we chose them over alternatives, and how we built a biotech data processing platform that uses cryptographic attestation to prove your data stays private (even from us).&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setting up NF Core with MicroK8s</title>
    <link rel="alternate" href="http://blog.url.com/blog/2025/02/01/setting-up-nf-core-with-microk8s/"/>
    <id>http://blog.url.com/blog/2025/02/01/setting-up-nf-core-with-microk8s/</id>
    <published>2025-02-01T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;This tutorial introduces a workflow system designed for scientific data processing. It showcases a leading solution that helps scientists streamline their computational needs through code-defined pipelines for data extraction and visualization. Following our previous blog posts, we&amp;rsquo;ll set up everything using a MicroK8s Kubernetes cluster to ensure workload scalability.&lt;/p&gt;

&lt;p&gt;We recommend reading our previous blog posts to familiarize yourself with the terminology and techniques used for provisioning and architecting the system. These posts also cover setting up a Docker-based DevOps environment to keep your local system clean.&lt;/p&gt;

&lt;h2&gt;Nextflow Basics&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nextflow&lt;/strong&gt; is a domain-specific language (DSL) and workflow manager designed to streamline &lt;strong&gt;data analysis pipelines&lt;/strong&gt; in &lt;strong&gt;high-performance computing (HPC), cloud, and containerized environments&lt;/strong&gt;. Built on &lt;strong&gt;Scala and Groovy&lt;/strong&gt;, it enables &lt;strong&gt;scalable, reproducible, and portable&lt;/strong&gt; execution of computational workflows.&lt;/p&gt;

&lt;h3&gt;&lt;strong&gt;Key Features of Nextflow:&lt;/strong&gt;&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pipeline Modularity&lt;/strong&gt; – Workflows are defined using a simple script format (&lt;code&gt;.nf&lt;/code&gt;), making them easy to develop and extend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallel Execution&lt;/strong&gt; – Nextflow automatically schedules and parallelizes tasks, efficiently utilizing available compute resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container Support&lt;/strong&gt; – Native support for &lt;strong&gt;Docker, Singularity, and Conda&lt;/strong&gt;, ensuring environment consistency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resumability &amp;amp; Fault Tolerance&lt;/strong&gt; – If a pipeline fails, Nextflow can &lt;strong&gt;resume from the last completed step&lt;/strong&gt;, reducing re-computation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud &amp;amp; HPC Integration&lt;/strong&gt; – Seamlessly runs on &lt;strong&gt;local machines, SLURM, SGE, Kubernetes, AWS Batch, and Google Cloud Life Sciences&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;&lt;strong&gt;Basic Nextflow Syntax:&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;A simple &lt;strong&gt;Nextflow script (&lt;code&gt;example.nf&lt;/code&gt;)&lt;/strong&gt; looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="n"&gt;HELLO&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;input:&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="nl"&gt;output:&lt;/span&gt;
    &lt;span class="n"&gt;stdout&lt;/span&gt;

    &lt;span class="nl"&gt;script:&lt;/span&gt;
    &lt;span class="s2"&gt;"""
    echo "Hello, $name!"
    """&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Charlie'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;HELLO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;You can run the script by running&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nextflow run example.nf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;One key thing to note is that all processes in nextflow are parralizable by default, so it’s important to understand what’s happening under the hood before implementing production grade workflows&lt;/li&gt;
&lt;li&gt;More information here &lt;a href="https://www.nextflow.io/docs/latest/your-first-script.html"&gt;https://www.nextflow.io/docs/latest/your-first-script.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;&lt;strong&gt;Why Nextflow for Scientific Workflows?&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Nextflow is widely used in &lt;strong&gt;bioinformatics, genomics, and large-scale data analysis&lt;/strong&gt; because it allows researchers to &lt;strong&gt;define workflows in code, scale workloads efficiently, and maintain reproducibility&lt;/strong&gt;. Combined with &lt;strong&gt;NF Core&lt;/strong&gt;, Nextflow ensures &lt;strong&gt;robust, versioned, and best-practice pipelines&lt;/strong&gt;, making it an excellent choice for &lt;strong&gt;scientific computing on Kubernetes&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;What is NF Core and Why Do We Care?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NF Core&lt;/strong&gt; is a community-driven framework for building and sharing best-practice computational pipelines using &lt;strong&gt;Nextflow&lt;/strong&gt;, a workflow manager optimized for scalable and reproducible data analysis. NF Core provides a standardized set of pipelines that are rigorously tested and maintained, ensuring robust execution across different computational environments, including local workstations, high-performance computing (HPC) clusters, and cloud-based infrastructure.&lt;/p&gt;

&lt;h3&gt;Why Use NF Core?&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reproducibility&lt;/strong&gt; – NF Core pipelines enforce strict version control, ensuring results remain consistent across different computational environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt; – Built with Nextflow, NF Core pipelines support seamless execution across multi-threaded, cluster-based, and cloud-based environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portability&lt;/strong&gt; – The pipelines are containerized using Docker, Singularity, or Conda, reducing dependency conflicts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community-Driven Best Practices&lt;/strong&gt; – Each pipeline adheres to strict guidelines, is thoroughly tested, and receives updates from the global research community.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;NF Core vs. Galaxy with CVMFS Drivers&lt;/h3&gt;

&lt;p&gt;Both NF Core and Galaxy are designed to facilitate scientific workflows, but they serve different use cases:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;NF Core (Nextflow)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Galaxy (CVMFS)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Experience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Command-line, highly scriptable&lt;/td&gt;
&lt;td&gt;Web-based GUI for ease of access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline Development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires coding (Nextflow DSL)&lt;/td&gt;
&lt;td&gt;Prebuilt tools with workflow assembly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highly scalable for cloud and HPC&lt;/td&gt;
&lt;td&gt;Scales well but requires CVMFS for distributed workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Works with containers (Docker/Singularity)&lt;/td&gt;
&lt;td&gt;CVMFS provides shared software repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong, pipeline-focused community&lt;/td&gt;
&lt;td&gt;Broad bioinformatics user base&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;h2&gt;DevOps Environment&lt;/h2&gt;

&lt;p&gt;As in previous tutorials, we&amp;rsquo;ll provision a DevOps Docker image that includes &lt;code&gt;Terraform&lt;/code&gt; and &lt;code&gt;AWS CLI&lt;/code&gt; for EC2 provisioning. These tools will suffice for this tutorial. You can follow our previous blog post&amp;rsquo;s guide on setting up the environment. Here&amp;rsquo;s what the provisioning script should look like.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# provision.sh&lt;/span&gt;

&lt;span class="nv"&gt;AWSCLI_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2.13.25
&lt;span class="nv"&gt;TERRAFORM_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.6.1

&lt;span class="k"&gt;function &lt;/span&gt;install_base_packages&lt;span class="o"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unzip wget curl openssh-client rsync
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;install_terraform&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp
    wget https://releases.hashicorp.com/terraform/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TERRAFORM_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/terraform_&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TERRAFORM_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;_linux_amd64.zip
    unzip terraform_&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TERRAFORM_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;_linux_amd64.zip
    &lt;span class="nb"&gt;sudo mv &lt;/span&gt;terraform /usr/local/bin/
    &lt;span class="nb"&gt;rm &lt;/span&gt;terraform_&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TERRAFORM_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;_linux_amd64.zip
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;install_aws_cli&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp
    curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64-&lt;/span&gt;&lt;span class="nv"&gt;$AWSCLI_VERSION&lt;/span&gt;&lt;span class="s2"&gt;.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscli.zip"&lt;/span&gt;
    unzip awscli.zip &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
    &lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
    &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; aws
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;main&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    install_base_packages
        install_terraform
        install_aws_cli
&lt;span class="o"&gt;}&lt;/span&gt;

main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Nextflow Configuration&lt;/h2&gt;

&lt;p&gt;We need to create a Persistent Volume (PV) and a Persistent Volume Claim for the Nextflow images to use. You may need to adjust the storage request size depending on your image&amp;rsquo;s claim size. Here&amp;rsquo;s what we&amp;rsquo;ll use in this tutorial:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persistent Volume&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolume&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextflow-pv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;capacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50Gi&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteMany&lt;/span&gt;
  &lt;span class="na"&gt;persistentVolumeReclaimPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextflow-storage&lt;/span&gt;
  &lt;span class="na"&gt;hostPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/work"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Persistent Volume Claim&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextflow-pvc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextflow&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteMany&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50Gi&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextflow-storage&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will need to define a &lt;code&gt;nextflow.config&lt;/code&gt; file that will configure how Nextflow runs pipelines in your instance. Here is the configuration we&amp;rsquo;ll use: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'k8s'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;k8s&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;storageClaimName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nextflow-pvc'&lt;/span&gt;
    &lt;span class="n"&gt;storageMountPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/home/ubuntu/work'&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nextflow'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The configuration specifies Kubernetes as the executor rather than the default local executor. It also defines the PVC to use, container mount paths, and target namespace.&lt;/p&gt;

&lt;p&gt;Inside your &lt;a href="http://setup.sh"&gt;&lt;code&gt;setup.sh&lt;/code&gt;&lt;/a&gt; script for EC2 provisioning, you&amp;rsquo;ll find the following method to set up NextFlow&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;install_nextflow&lt;span class="o"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noninteractive apt &lt;span class="nb"&gt;install &lt;/span&gt;openjdk-11-jdk &lt;span class="nt"&gt;-y&lt;/span&gt;
    curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://get.nextflow.io | bash
    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x nextflow
    &lt;span class="nb"&gt;sudo mv &lt;/span&gt;nextflow /usr/local/bin/

    &lt;span class="c"&gt;# K8s Config for root&lt;/span&gt;
    &lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /root/.kube
    &lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /var/snap/microk8s/current/credentials/client.config /root/.kube/config

    &lt;span class="c"&gt;# Setup up PV and PVCs&lt;/span&gt;
    microk8s kubectl create namespace nextflow
    microk8s kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; nextflow-pv.yaml
    microk8s kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; nextflow-pvc.yaml &lt;span class="nt"&gt;-n&lt;/span&gt; nextflow

    &lt;span class="c"&gt;# Install FUSE plugin - Not used right now - better practice?&lt;/span&gt;
    &lt;span class="c"&gt;# kubectl create -f https://github.com/nextflow-io/k8s-fuse-plugin/raw/master/manifests/k8s-fuse-plugin.yml&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Following NextFlow&amp;rsquo;s installation instructions, we set up the required namespaces, Persistent Volume, and Persistent Volume Claim for pipeline execution. While NextFlow recommends using FUSE to deploy and run workflows, we&amp;rsquo;ve included but commented out the FUSE plugin installation for MicroK8s, as we&amp;rsquo;re not currently using it. &lt;/p&gt;

&lt;p&gt;We are using a very similar &lt;a href="http://setup.sh"&gt;&lt;code&gt;setup.sh&lt;/code&gt;&lt;/a&gt; script as before, the main method does the following&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    install_microk8s
    install_nfs
    setup_microK8s
    update_ip_in_microk8s_config
    update_ip_in_kubeconfig
    install_nextflow
    install_docker
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All of these methods can be found in the previous blog posts.&lt;/p&gt;

&lt;h2&gt;Deploying EC2 Instance&lt;/h2&gt;

&lt;p&gt;We&amp;rsquo;ll again use Terraform to deploy our EC2 instance, with additional files being transferred to the instance after provisioning. The security rules remain similar to our previous blog post. Below is the complete Terraform script. We&amp;rsquo;ve selected an &lt;code&gt;m5.4xlarge&lt;/code&gt; instance type for this tutorial since many nf-core workflows require substantial CPU resources.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="s2"&gt;"myip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://ipv4.icanhazip.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;owners&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"099720109477"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"microk8s-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Security group for MicroK8s EC2 instance"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;

  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow SSH access from your public IP"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16443&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16443&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow K8s access from your public IP"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_id&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_sg&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;

  &lt;span class="nx"&gt;root_block_device&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="nx"&gt;volume_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gp2"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
    &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/setup.sh"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/setup.sh"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/nextflow-pv.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/nextflow-pv.yaml"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/nextflow-pvc.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/nextflow-pvc.yaml"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/nextflow.config"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/nextflow.config"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo bash /home/ubuntu/setup.sh"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"microk8s-dev-instance"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can now deploy the instance by running the following&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

terraform init
terraform validate
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qc-microk8s-dev-plan
terraform apply qc-microk8s-dev-plan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Running our First NextFlow Workflow&lt;/h2&gt;

&lt;p&gt;Once everything has been deployed, we can connect to our EC2 instance using our &lt;code&gt;./connect.sh&lt;/code&gt; script. Once connected, we test that everything worked correctly by running the following commands&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NextFlow has installed correctly&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nextflow info

&lt;span class="c"&gt;########################## OUTPUT ##########################&lt;/span&gt;
  Version: 24.10.3 build 5933
  Created: 16-12-2024 15:34 UTC 
  System: Linux 5.15.0-1072-aws
  Runtime: Groovy 4.0.23 on OpenJDK 64-Bit Server VM 11.0.25+9-post-Ubuntu-1ubuntu120.04
  Encoding: UTF-8 &lt;span class="o"&gt;(&lt;/span&gt;UTF-8&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;MicroK8s is running&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s status

microk8s is running
high-availability: no
  datastore master nodes: 127.0.0.1:19001
  datastore standby nodes: none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the above two are good, now we can attempt to run our first workflow. If you run the command in the directory of the &lt;code&gt;nextflow.config&lt;/code&gt; file, you should see some fun stuff happening!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nextflow run hello

&lt;span class="c"&gt;########################## OUTPUT ##########################&lt;/span&gt;
 N E X T F L O W   ~  version 24.10.3

Launching &lt;span class="sb"&gt;`&lt;/span&gt;https://github.com/nextflow-io/hello&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;astonishing_caravaggio] DSL2 - revision: afff16a9b4 &lt;span class="o"&gt;[&lt;/span&gt;master]

executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Important that this does NOT say `local`&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;fd/f7ba24] sayHello &lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;100%] 4 of 4 ✔
Bonjour world!

Ciao world!

Hello world!

Hola world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you get the above output running the hello image AND you have the &lt;code&gt;k8s&lt;/code&gt; executer running, then you are in great shape. If this is not working, ensure your config matches up with what is here and that you are running it in the same directory at the config file.&lt;/p&gt;

&lt;h2&gt;Running our First NF Core Workflow&lt;/h2&gt;

&lt;p&gt;As mentioned before, NF Core is community driven by scientists around the world. As a result, there are hundreds are pre-built pipelines that can be used. All of the pipelines can be found here&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nf-co.re/pipelines/"&gt;https://nf-co.re/pipelines/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are not subject matter experts in the field, but we do know how to set up configurations, so just going over the basic pipelines, there is a demo pipeline we can attempt to use in order to ensure that our K8s setup is correct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nf-co.re/demo/1.0.1/"&gt;demo: Introduction&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we have the &lt;code&gt;samplesheet.csv&lt;/code&gt; file as our input to the pipeline. We need to update the &lt;code&gt;nextflow.config&lt;/code&gt; file slightly&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'k8s'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;k8s&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;storageClaimName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nextflow-pvc'&lt;/span&gt;
    &lt;span class="n"&gt;storageMountPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/home/ubuntu/work'&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nextflow'&lt;/span&gt;
    &lt;span class="n"&gt;pod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="c1"&gt;// required as the pipeline performs a sym-link&lt;/span&gt;
        &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;volumeClaim:&lt;/span&gt; &lt;span class="s2"&gt;"nextflow-pvc"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;mountPath:&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/work"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;hostPath:&lt;/span&gt; &lt;span class="s2"&gt;"/root/.nextflow/assets"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;mountPath:&lt;/span&gt; &lt;span class="s2"&gt;"/root/.nextflow/assets"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should also create an &lt;code&gt;output&lt;/code&gt; directory&lt;/p&gt;

&lt;p&gt;Now we can run the following&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nextflow run nf-core/demo &lt;span class="nt"&gt;--input&lt;/span&gt; samplesheet.csv &lt;span class="nt"&gt;--outdir&lt;/span&gt; output

&lt;span class="c"&gt;########################## OUTPUT ##########################&lt;/span&gt;
 N E X T F L O W   ~  version 24.10.3

Launching &lt;span class="sb"&gt;`&lt;/span&gt;https://github.com/nf-core/demo&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;nice_fourier] DSL2 - revision: 04060b4644 &lt;span class="o"&gt;[&lt;/span&gt;master]

&lt;span class="nt"&gt;------------------------------------------------------&lt;/span&gt;
                                        ,--./,-.
        ___     __   __   __   ___     /,-._.--~&lt;span class="s1"&gt;'
  |\ | |__  __ /  ` /  \ |__) |__         }  {
  | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                        `._,._,'&lt;/span&gt;
  nf-core/demo 1.0.1
&lt;span class="nt"&gt;------------------------------------------------------&lt;/span&gt;
Input/output options
  input      : samplesheet.csv
  outdir     : output

Core Nextflow options
  revision   : master
  runName    : nice_fourier
  launchDir  : /home/ubuntu
  workDir    : /home/ubuntu/work
  projectDir : /root/.nextflow/assets/nf-core/demo
  userName   : root
  profile    : standard
  configFiles: 

&lt;span class="o"&gt;!!&lt;/span&gt; Only displaying parameters that differ from the pipeline defaults &lt;span class="o"&gt;!!&lt;/span&gt;
&lt;span class="nt"&gt;------------------------------------------------------&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; The pipeline
  https://doi.org/10.5281/zenodo.12192442

&lt;span class="k"&gt;*&lt;/span&gt; The nf-core framework
    https://doi.org/10.1038/s41587-020-0439-x

&lt;span class="k"&gt;*&lt;/span&gt; Software dependencies
    https://github.com/nf-core/demo/blob/master/CITATIONS.md

executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt;
executor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  k8s &lt;span class="o"&gt;(&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;d6/384cf0] NFCORE_DEMO:DEMO:FASTQC &lt;span class="o"&gt;(&lt;/span&gt;SAMPLE1_PE&lt;span class="o"&gt;)&lt;/span&gt;     &lt;span class="o"&gt;[&lt;/span&gt;100%] 3 of 3 ✔
&lt;span class="o"&gt;[&lt;/span&gt;db/180458] NFCORE_DEMO:DEMO:SEQTK_TRIM &lt;span class="o"&gt;(&lt;/span&gt;SAMPLE1_PE&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;100%] 3 of 3 ✔
&lt;span class="o"&gt;[&lt;/span&gt;1a/799111] NFCORE_DEMO:DEMO:MULTIQC                 &lt;span class="o"&gt;[&lt;/span&gt;100%] 1 of 1 ✔
-[nf-core/demo] Pipeline completed successfully-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you monitor your cluster, you should see that pods start spinning up and completed their jobs. You can then go to the &lt;code&gt;output&lt;/code&gt; directory and verify that data and result html files are there.&lt;/p&gt;

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

&lt;p&gt;In this tutorial, we demonstrated how to set up and execute &lt;strong&gt;Nextflow&lt;/strong&gt; workflows on a &lt;strong&gt;MicroK8s Kubernetes cluster&lt;/strong&gt;, leveraging &lt;strong&gt;persistent storage&lt;/strong&gt; and &lt;strong&gt;containerized execution&lt;/strong&gt; to ensure scalability and reproducibility. We covered the &lt;strong&gt;basics of Nextflow&lt;/strong&gt;, including its advantages for scientific workflows, and explored &lt;strong&gt;NF Core&lt;/strong&gt;, a community-driven repository of curated pipelines.&lt;/p&gt;

&lt;p&gt;By provisioning our infrastructure with &lt;strong&gt;Terraform&lt;/strong&gt;, configuring &lt;strong&gt;Nextflow on Kubernetes&lt;/strong&gt;, and successfully running both &lt;strong&gt;Nextflow&amp;rsquo;s Hello World workflow&lt;/strong&gt; and an &lt;strong&gt;NF Core demo pipeline&lt;/strong&gt;, we validated our deployment. These workflows illustrate how computational pipelines can be streamlined, modularized, and efficiently managed in a cloud-native environment.&lt;/p&gt;

&lt;p&gt;This setup provides a &lt;strong&gt;solid foundation&lt;/strong&gt; for running complex data analysis workflows in genomics, bioinformatics, and other scientific domains. Whether you&amp;rsquo;re processing large datasets, integrating various bioinformatics tools, or scaling workloads across cloud and on-premise clusters, &lt;strong&gt;Nextflow and Kubernetes provide a powerful combination&lt;/strong&gt; for computational reproducibility.&lt;/p&gt;

&lt;p&gt;As a next step, you can explore &lt;strong&gt;customizing Nextflow workflows&lt;/strong&gt;, integrating &lt;strong&gt;cloud storage (e.g., AWS S3, Google Cloud Storage)&lt;/strong&gt;, or optimizing &lt;strong&gt;resource allocation for large-scale datasets&lt;/strong&gt;. If you encounter any challenges, feel free to reach out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Use Case: The Galaxy Project</title>
    <link rel="alternate" href="http://blog.url.com/blog/2025/01/15/use-case-the-galaxy-project/"/>
    <id>http://blog.url.com/blog/2025/01/15/use-case-the-galaxy-project/</id>
    <published>2025-01-15T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;p&gt;Previous Section: &lt;a href="/blog/2025/01/14/putting-it-all-together/"&gt;Putting it All Together: CVMFS In Action&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Galaxy Project&lt;/h2&gt;

&lt;p&gt;In the era of big data, researchers across scientific disciplines require robust, scalable, and user-friendly platforms to analyze complex datasets. The &lt;strong&gt;Galaxy Project&lt;/strong&gt; is an open-source, web-based computational workbench designed to empower researchers by providing accessible, reproducible, and transparent data analysis. Originally developed for &lt;strong&gt;bioinformatics and genomics&lt;/strong&gt;, Galaxy has since evolved into a &lt;strong&gt;general-purpose platform&lt;/strong&gt; for a wide range of scientific workflows, spanning domains such as &lt;strong&gt;climate science, cheminformatics, and machine learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At its core, Galaxy simplifies complex computational tasks by offering an &lt;strong&gt;interactive graphical user interface (GUI)&lt;/strong&gt;, eliminating the need for researchers to have deep programming knowledge. It also supports &lt;strong&gt;workflow automation&lt;/strong&gt;, allowing users to build and share reproducible pipelines effortlessly. Galaxy’s extensibility is powered by a &lt;strong&gt;rich ecosystem of tools&lt;/strong&gt;, a thriving community, and integrations with cloud and high-performance computing (HPC) environments.&lt;/p&gt;

&lt;p&gt;With deployments ranging from &lt;strong&gt;local installations to large-scale Kubernetes-based infrastructures&lt;/strong&gt;, Galaxy ensures that researchers can harness computational power efficiently, regardless of their expertise level. Whether running on &lt;strong&gt;personal servers, institutional clusters, or public clouds&lt;/strong&gt;, Galaxy facilitates seamless collaboration, data sharing, and robust computational analysis.&lt;/p&gt;

&lt;p&gt;In this post, we will use the skills we’ve gained to set up CVMFS drivers and allow us to set up a cluster that runs the Galaxy Project and process some demo data.&lt;/p&gt;

&lt;h2&gt;Chart Resources&lt;/h2&gt;

&lt;p&gt;If you have not read the previous blog posts, it is strongly advised that you do in order to gain a strong understanding of the DevOps technologies we are using to deploy this project.&lt;/p&gt;

&lt;h3&gt;Terraform Files&lt;/h3&gt;

&lt;p&gt;We will continue to use our &lt;code&gt;main.tf&lt;/code&gt;  as the backbone to the operations of deploying an EC2 Instance and Provisioning it using Terraform.  In this file, we will also add an elastic IP that will remain static so that if we need to take down the instance and bring it back up, the IP will still remain the same to point to the Galaxy project webpage. Another major difference is that we will be running our provisioning scripts after the EIP is assigned, this is due to the fact that we require the IP address in order to connect to the cluster locally via Lens.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="s2"&gt;"myip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://ipv4.icanhazip.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;owners&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"099720109477"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"microk8s-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Security group for MicroK8s EC2 instance"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;

  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow SSH access from your public IP"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16443&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16443&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow K8s access from your public IP"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow port-forward access to Galaxy service"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow HTTP access on port 80"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c1"&gt;# Allow access to the NodePort (e.g., 32000)&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web_node_port&lt;/span&gt;                         
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web_node_port&lt;/span&gt;                         
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;your_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"${chomp(data.http.myip.response_body)}/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow public access to NodePort service"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Step 1: Allocate the Elastic IP&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_eip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_id&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_sg&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;

  &lt;span class="nx"&gt;root_block_device&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="nx"&gt;volume_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gp2"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"microk8s-dev-instance"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip_association"&lt;/span&gt; &lt;span class="s2"&gt;"microk8s_eip_assoc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;instance_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_instance&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;allocation_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"post_eip_provisioning"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_eip_association&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip_assoc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/setup.sh"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/setup.sh"&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/cvmfs-values.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/cvmfs-values.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/galaxy-pvc.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/galaxy-pvc.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/galaxy-values.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/galaxy-values.yaml"&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/galaxy-services.sh"&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/galaxy-services.sh"&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo bash /home/ubuntu/setup.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo chmod +x /home/ubuntu/galaxy-services.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
      &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Routing (Optional)&lt;/h3&gt;

&lt;p&gt;To simplify access to our Galaxy Project instance, we will configure a domain name, &lt;a href="http://galaxy.lablytics.com/"&gt;&lt;code&gt;galaxy.lablytics.com&lt;/code&gt;&lt;/a&gt;, for easy navigation.&lt;/p&gt;

&lt;p&gt;To achieve this, you will need to create a &lt;strong&gt;hosted zone&lt;/strong&gt; within your DNS provider. Alternatively, you can define the hosted zone directly in your infrastructure code for a more automated setup.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;routes.tf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#Route 53 A Record for NodePort service using the Elastic IP&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_zone"&lt;/span&gt; &lt;span class="s2"&gt;"zone"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lablytics.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_record"&lt;/span&gt; &lt;span class="s2"&gt;"nodeport_service"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain_name&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A"&lt;/span&gt;
  &lt;span class="nx"&gt;ttl&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_record"&lt;/span&gt; &lt;span class="s2"&gt;"www_nodeport_service"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"www.${var.domain_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A"&lt;/span&gt;
  &lt;span class="nx"&gt;ttl&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microk8s_eip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Setting Up the Provisioning Process&lt;/h3&gt;

&lt;p&gt;To provision and configure the CVMFS drivers, we will use the &lt;a href="http://setup.sh/"&gt;&lt;code&gt;setup.sh&lt;/code&gt;&lt;/a&gt; script, which follows the standard approach outlined in the official CVMFS driver tutorials. This script ensures that the necessary drivers are installed and ready for use. You can find our version of the setup script in our public repository here: &lt;strong&gt;[TODO: Insert link]&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;Provisioning Galaxy Services&lt;/h3&gt;

&lt;p&gt;We will create a &lt;code&gt;galaxy-services.sh&lt;/code&gt; script that will be executed directly within the EC2 instance. This approach is necessary because performing Helm updates over an SSH connection initiated by Terraform can sometimes result in connectivity issues.&lt;/p&gt;

&lt;p&gt;The script itself is straightforward. It installs CVMFS and deploys the Galaxy Project using predefined configuration values. By running this script within the EC2 instance, we ensure a more reliable provisioning process without encountering SSH-related limitations.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;install_cvmfs&lt;span class="o"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing CVMFS on K8s..."&lt;/span&gt;
    git clone https://github.com/lablytics/cvmfs-csi /home/ubuntu/cvmfs-csi
    microk8s helm3 &lt;span class="nb"&gt;install &lt;/span&gt;cvmfs /home/ubuntu/cvmfs-csi/deployments/helm/cvmfs-csi &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-f&lt;/span&gt; /home/ubuntu/cvmfs-values.yaml
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CVMFS installed successfully"&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;120
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;install_galaxy&lt;span class="o"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing Galaxy on K8s..."&lt;/span&gt;
    microk8s kubectl create namespace galaxy
    microk8s kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; /home/ubuntu/galaxy-pvc.yaml 
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sleeping for 15 seconds..."&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;15
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waking up..."&lt;/span&gt;

    &lt;span class="c"&gt;# Install Galaxy&lt;/span&gt;
    git clone https://github.com/galaxyproject/galaxy-helm.git /home/ubuntu/galaxy-helm
    &lt;span class="nb"&gt;cd&lt;/span&gt; /home/ubuntu/galaxy-helm/galaxy
    git checkout 2557baac75ee56c8c9801946c9ee8d633db7d56e &lt;span class="c"&gt;# Checkout to the version that works with the current Galaxy version&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s helm3 dependency update
    microk8s helm3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; galaxy galaxy-project /home/ubuntu/galaxy-helm/galaxy &lt;span class="nt"&gt;-f&lt;/span&gt; /home/ubuntu/galaxy-values.yaml &lt;span class="nt"&gt;--timeout&lt;/span&gt; 1m30s
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Galaxy installed successfully"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;main&lt;span class="o"&gt;(){&lt;/span&gt;
    install_cvmfs
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sleeping for 30 seconds..."&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;30
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waking up..."&lt;/span&gt;
    install_galaxy
&lt;span class="o"&gt;}&lt;/span&gt;

main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Value Files&lt;/h3&gt;

&lt;p&gt;We will need to update our &lt;code&gt;cvmfs-values.yaml&lt;/code&gt; file as well as create a &lt;code&gt;galaxy-values.yaml&lt;/code&gt; configuration file for the Galaxy deployment. As you’ll notice in the &lt;code&gt;cvmfs-values.yaml&lt;/code&gt; , there are specific galaxy repositories here that we will require access to.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# cvmfs-values.yaml&lt;/span&gt;
&lt;span class="na"&gt;extraConfigMaps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cvmfs-csi-default-local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default.local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_HTTP_PROXY="DIRECT"&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_QUOTA_LIMIT="4000"&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_USE_GEOAPI="yes"&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_AUTOFS_TIMEOUT=3600&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_DEBUGLOG=/tmp/cvmfs.log&lt;/span&gt;

      &lt;span class="s"&gt;{{- if .Values.cache.alien.enabled }}&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_ALIEN_CACHE={{ .Values.cache.alien.location }}&lt;/span&gt;
      &lt;span class="s"&gt;# When alien cache is used, CVMFS does not control the size of the cache.&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_QUOTA_LIMIT=-1&lt;/span&gt;
      &lt;span class="s"&gt;# Whether repositories should share a cache directory or each have their own.&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_SHARED_CACHE=no&lt;/span&gt;
      &lt;span class="s"&gt;{{- end -}}&lt;/span&gt;
  &lt;span class="na"&gt;cvmfs-csi-config-d&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;data.galaxyproject.org.conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_SERVER_URL="http://cvmfs1-iu0.galaxyproject.org/cvmfs/@fqrn@;http://cvmfs1-tacc0.galaxyproject.org/cvmfs/@fqrn@;http://cvmfs1-psu0.galaxyproject.org/cvmfs/@fqrn@;http://cvmfs1-mel0.gvl.org.au/cvmfs/@fqrn@;http://cvmfs1-ufr0.galaxyproject.eu/cvmfs/@fqrn@"&lt;/span&gt;
      &lt;span class="s"&gt;CVMFS_PUBLIC_KEY="/etc/cvmfs/config.d/data.galaxyproject.org.pub"&lt;/span&gt;
    &lt;span class="na"&gt;data.galaxyproject.org.pub&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;-----BEGIN PUBLIC KEY-----&lt;/span&gt;
      &lt;span class="s"&gt;MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5LHQuKWzcX5iBbCGsXGt&lt;/span&gt;
      &lt;span class="s"&gt;6CRi9+a9cKZG4UlX/lJukEJ+3dSxVDWJs88PSdLk+E25494oU56hB8YeVq+W8AQE&lt;/span&gt;
      &lt;span class="s"&gt;3LWx2K2ruRjEAI2o8sRgs/IbafjZ7cBuERzqj3Tn5qUIBFoKUMWMSIiWTQe2Sfnj&lt;/span&gt;
      &lt;span class="s"&gt;GzfDoswr5TTk7aH/FIXUjLnLGGCOzPtUC244IhHARzu86bWYxQJUw0/kZl5wVGcH&lt;/span&gt;
      &lt;span class="s"&gt;maSgr39h1xPst0Vx1keJ95AH0wqxPbCcyBGtF1L6HQlLidmoIDqcCQpLsGJJEoOs&lt;/span&gt;
      &lt;span class="s"&gt;NVNhhcb66OJHah5ppI1N3cZehdaKyr1XcF9eedwLFTvuiwTn6qMmttT/tHX7rcxT&lt;/span&gt;
      &lt;span class="s"&gt;owIDAQAB&lt;/span&gt;
      &lt;span class="s"&gt;-----END PUBLIC KEY-----&lt;/span&gt;

&lt;span class="na"&gt;automountHostPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cvmfs&lt;/span&gt;
&lt;span class="na"&gt;kubeletDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/snap/microk8s/common/var/lib/kubelet&lt;/span&gt;

&lt;span class="na"&gt;automountStorageClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cvmfs&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# galaxy-values.yaml&lt;/span&gt;
&lt;span class="na"&gt;cvmfs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;persistence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;existingClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;galaxy-pvc"&lt;/span&gt;

&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/tls-acme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/training-material"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;

&lt;p&gt;Now we can create the EC2 instance by running the &lt;code&gt;.create.sh&lt;/code&gt; script that we previously wrote. This will tell Terraform to initialize, create a plan, validate it and finally create our AWS resources required to deploy the Galaxy Project.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

terraform init
terraform validate
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qc-microk8s-dev-plan
terraform apply qc-microk8s-dev-plan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once our instance has been successfully created and provisioned, we can establish an SSH connection using the &lt;code&gt;connect.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;In this iteration, we’ve made a small but impactful change from the previous tutorial: the Kubernetes configuration (&lt;code&gt;kubeconfig&lt;/code&gt;) is now copied to a location that Lens can automatically sync with. This is achieved by setting up a mount from our local system, where Lens expects the configuration, to the Docker DevOps container.&lt;/p&gt;

&lt;p&gt;As a result, when we transfer the &lt;code&gt;kubeconfig&lt;/code&gt; from the EC2 instance to the Docker container using &lt;code&gt;scp&lt;/code&gt;, the mount ensures that our local machine also has direct access to the file. This streamlines the workflow, eliminating the need for additional manual steps.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Set your AWS region&lt;/span&gt;
&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;  &lt;span class="c"&gt;# Change this to the region where your EC2 instance is running&lt;/span&gt;

&lt;span class="c"&gt;# Set your EC2 key pair path&lt;/span&gt;
&lt;span class="nv"&gt;KEY_PAIR_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"../devbox-key-pair.pem"&lt;/span&gt;  &lt;span class="c"&gt;# Change this to the correct path to your key pair&lt;/span&gt;

&lt;span class="c"&gt;# Set your EC2 tag name to find the instance&lt;/span&gt;
&lt;span class="nv"&gt;INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"microk8s-dev-instance"&lt;/span&gt;

&lt;span class="c"&gt;# Get the public IP of the instance with the tag name 'microk8s-dev-instance'&lt;/span&gt;
&lt;span class="nv"&gt;INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=tag:Name,Values=&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"Name=instance-state-name,Values=running"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Reservations[*].Instances[*].PublicIpAddress"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Check if an IP address was found&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instance with tag name '&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="s2"&gt;' is not running or doesn't exist."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Connect to the instance via SSH&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connecting to EC2 instance with IP: &lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

scp &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_PAIR_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ubuntu@&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;:/home/ubuntu/.kube/config-public /configs/microk8s-config
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_PAIR_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ubuntu@&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you are connected to the instance, you should be able to run the &lt;code&gt;./galaxy-services.sh&lt;/code&gt; script which installs everything you need. This does take a few minutes (around 5 or so), but once everything is running, you should see something like this&lt;/p&gt;

&lt;p&gt;Lens&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/15/use-case-the-galaxy-project/image1.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;p&gt;CLI&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ubuntu@ip-172-31-85-199:~/galaxy-helm/galaxy$ kubectl get pods -n galaxy
NAME                                                              READY   STATUS    RESTARTS   AGE
galaxy-galaxy-project-postgres-0                                  1/1     Running   0          41m
galaxy-project-celery-5f95bf89bd-txsqd                            1/1     Running   0          41m
galaxy-project-celery-beat-7cf5f8857c-87k2q                       1/1     Running   0          41m
galaxy-project-job-0-64964cf69c-rtkkv                             1/1     Running   0          41m
galaxy-project-nginx-76cf7cc5bf-9bkvs                             1/1     Running   0          41m
galaxy-project-post-install-cvmfs-fix-job-jst2j                   1/1     Running   0          41m
galaxy-project-postgres-6f8c745954-8fbnk                          1/1     Running   0          41m
galaxy-project-rabbitmq-7bfcd857c9-99tx8                          1/1     Running   0          41m
galaxy-project-rabbitmq-messaging-topology-operator-65f67ccr5n7   1/1     Running   0          41m
galaxy-project-rabbitmq-server-server-0                           1/1     Running   0          41m
galaxy-project-tusd-65b6b64df6-kq8v8                              1/1     Running   0          41m
galaxy-project-web-6bc44df94d-qvqv8                               1/1     Running   0          41m
galaxy-project-workflow-7cd9dbd78c-lkmhp                          1/1     Running   0          41m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Going through the demo&lt;/h2&gt;

&lt;p&gt;In order to ensure everything is working correct, follow the tutorial provided by Galaxy&lt;/p&gt;

&lt;p&gt;&lt;a href="https://training.galaxyproject.org/training-material/topics/introduction/tutorials/galaxy-intro-short/tutorial.html"&gt;https://training.galaxyproject.org/training-material/topics/introduction/tutorials/galaxy-intro-short/tutorial.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, in order to access the web UI, we need to do one of the following.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Forward SSH Tunnel to the WebUI Pod&lt;/li&gt;
&lt;li&gt;Go to the IP address of the EC2 Instance&lt;/li&gt;
&lt;li&gt;Go to the assigned domain name of the EC2 Instance&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Forward SSH Tunnel&lt;/h3&gt;

&lt;p&gt;Lens&lt;/p&gt;

&lt;p&gt;In Lens, go to the &lt;code&gt;galaxy-project-web-...&lt;/code&gt;  pod, and open it.  If you scroll down to &lt;strong&gt;Containers, then Port,&lt;/strong&gt; you should see that the container is running on port 8080. There is a blue &lt;code&gt;Forward…&lt;/code&gt; button that allows you to port forward to the pod instance and access it. Once you set that up, you should be able to view &lt;a href="http://localhost:49323/"&gt;&lt;code&gt;http://localhost:[ASSIGNED_PORT]/&lt;/code&gt;&lt;/a&gt; and see the Galaxy UI.&lt;/p&gt;

&lt;p&gt;CLI&lt;/p&gt;

&lt;p&gt;You can run the following command to port forward &lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl port-forward pod/&amp;lt;POD_NAME&amp;gt; &amp;lt;LOCAL_PORT&amp;gt;:8080 -n galaxy&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;If you are able to reach the UI, you should see something like&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/15/use-case-the-galaxy-project/image2.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;p&gt;If you are able to get through the entire demo, then you are done and have everything set up!&lt;/p&gt;

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

&lt;p&gt;By following this guide, we have successfully deployed the &lt;strong&gt;Galaxy Project&lt;/strong&gt; on an EC2 instance using &lt;strong&gt;Terraform, Kubernetes, Helm, and CVMFS&lt;/strong&gt;. This setup provides a scalable and reproducible environment for researchers and data scientists to leverage Galaxy’s powerful workflow automation and computational capabilities.&lt;/p&gt;

&lt;p&gt;We began by provisioning our infrastructure with &lt;strong&gt;Terraform&lt;/strong&gt;, ensuring that our EC2 instance was correctly configured with an &lt;strong&gt;Elastic IP&lt;/strong&gt; for persistent access. We then deployed &lt;strong&gt;CVMFS drivers&lt;/strong&gt;, enabling efficient data access across distributed environments, and installed &lt;strong&gt;Galaxy&lt;/strong&gt; within a Kubernetes cluster. Through the use of &lt;strong&gt;Helm charts and predefined configuration files&lt;/strong&gt;, we automated the deployment process, ensuring a seamless setup.&lt;/p&gt;

&lt;p&gt;To simplify access, we configured a &lt;strong&gt;hosted domain name&lt;/strong&gt; and explored different methods to connect to the &lt;strong&gt;Galaxy Web UI&lt;/strong&gt;, including &lt;strong&gt;port forwarding via SSH&lt;/strong&gt;, direct access through the instance’s &lt;strong&gt;public IP&lt;/strong&gt;, and routing through a &lt;strong&gt;custom domain&lt;/strong&gt;. Once everything was set up, we validated our deployment by running a sample workflow from the &lt;strong&gt;Galaxy training materials&lt;/strong&gt;, confirming that the system was operational.&lt;/p&gt;

&lt;p&gt;With this foundation in place, you now have a fully functional Galaxy instance running in a &lt;strong&gt;cloud-based, containerized environment&lt;/strong&gt;. This setup not only enables high-performance data analysis but also ensures &lt;strong&gt;scalability, reproducibility, and ease of collaboration&lt;/strong&gt;. Whether you&amp;rsquo;re using Galaxy for &lt;strong&gt;bioinformatics, cheminformatics, climate science, or machine learning&lt;/strong&gt;, this deployment provides the flexibility and power needed for modern scientific computing.&lt;/p&gt;

&lt;p&gt;Now that your Galaxy instance is up and running, you can start exploring its vast ecosystem of tools and workflows. If you encounter any issues, feel free to reach out.&lt;/p&gt;

&lt;h3&gt;Code Reference&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lablytics/cvmfs-csi/tree/master/devops/cvmfs-full-setup-basic-galaxy"&gt;https://github.com/lablytics/cvmfs-csi/tree/master/devops/cvmfs-full-setup-basic-galaxy&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Putting it All Together: CVMFS In Action</title>
    <link rel="alternate" href="http://blog.url.com/blog/2025/01/14/putting-it-all-together/"/>
    <id>http://blog.url.com/blog/2025/01/14/putting-it-all-together/</id>
    <published>2025-01-14T00:00:00+00:00</published>
    <updated>2026-08-09T16:43:59+00:00</updated>
    <author>
      <name>Article Author</name>
    </author>
    <content type="html">&lt;p&gt;Previous Section: &lt;a href="/blog/2025/01/13/kubernetes-and-helm/"&gt;Kubernetes and Helm: The Beauty of Orchestration&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are finally at the place where we start to see all of our hard work pay off! This is the point where we continue to automate the creation of our resources as well as connect to our cluster pods and see CVMFS in action. It is assumed that ALL of the previous sections have been read.&lt;/p&gt;

&lt;h2&gt;Why all the Automation?&lt;/h2&gt;

&lt;p&gt;Manual setup of cloud infrastructure can be both time-consuming and error-prone. Automating these tasks not only ensures consistency but also accelerates the deployment process, making your infrastructure scalable and easily reproducible. By using a script to automate the deployment of resources, you minimize the risks associated with human error and improve efficiency.&lt;/p&gt;

&lt;p&gt;The automation provided here serves two purposes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Every time you run the script, it ensures that the environment is set up in exactly the same way. This eliminates discrepancies that can arise from manual setup, where missed steps or subtle differences can lead to unexpected behavior.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: Manual deployment requires time and detailed knowledge of each step involved, whereas an automated script performs all actions in sequence, reducing the overall setup time. This is particularly useful when deploying multiple environments (e.g., for staging, testing, and production), or when you need to make infrastructure adjustments and roll out updates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you&amp;rsquo;re curious to dive deeper into the underlying processes, feel free to execute each step manually and explore the commands used in the script. Understanding what each command does will give you a solid foundation in how the cloud infrastructure is built and managed.&lt;/p&gt;

&lt;h2&gt;Deploy our EC2 Instance With Terraform&lt;/h2&gt;

&lt;p&gt;In this script, we’ll automate the setup and deployment of an EC2 instance using Terraform through a simple Bash script. This script automates key Terraform steps: initialization, validation, planning, and deployment, making the process efficient and consistent.&lt;/p&gt;

&lt;h3&gt;Prerequisites&lt;/h3&gt;

&lt;p&gt;Before running this script, ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a local PEM key pair (replace &lt;code&gt;YOUR_KEY_PAIR.pem&lt;/code&gt; with your actual key).&lt;/li&gt;
&lt;li&gt;Terraform is installed on your machine.&lt;/li&gt;
&lt;li&gt;AWS credentials are configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Script Overview: &lt;code&gt;create.sh&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The script performs the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Set Key Permissions&lt;/strong&gt;: Secures the PEM key for SSH access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Initialize Terraform&lt;/strong&gt;: Prepares Terraform by downloading providers and setting up configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validate Configuration&lt;/strong&gt;: Checks for syntax and structural errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan Deployment&lt;/strong&gt;: Previews resources to be created or modified.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply the Plan&lt;/strong&gt;: Deploys resources as per the saved plan.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Script Example&lt;/h3&gt;

&lt;p&gt;Save this script as &lt;code&gt;create.sh&lt;/code&gt; in your Terraform project directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create.sh&lt;/span&gt;

&lt;span class="nb"&gt;chmod &lt;/span&gt;400 ../YOUR_KEY_PAIR.pem  &lt;span class="c"&gt;# Secure the PEM key&lt;/span&gt;

terraform init                   &lt;span class="c"&gt;# Initialize Terraform&lt;/span&gt;
terraform validate               &lt;span class="c"&gt;# Validate configuration&lt;/span&gt;
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qc-microk8s-dev-plan  &lt;span class="c"&gt;# Save the plan&lt;/span&gt;
terraform apply qc-microk8s-dev-plan      &lt;span class="c"&gt;# Execute deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This script provides a reliable, automated way to deploy infrastructure using Terraform.&lt;/p&gt;

&lt;p&gt;We can run the script by&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./create.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will take a few minutes to provision depending on the instance size you are using, but when its all done, you should see the following few messages.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_instance.microk8s_instance (remote-exec): Resolving deltas: 100% (6318/6318)
aws_instance.microk8s_instance (remote-exec): Resolving deltas: 100% (6318/6318), done.
aws_instance.microk8s_instance (remote-exec): NAME: cvmfs
aws_instance.microk8s_instance (remote-exec): LAST DEPLOYED: Mon Nov 11 21:17:34 2024
aws_instance.microk8s_instance (remote-exec): NAMESPACE: kube-system
aws_instance.microk8s_instance (remote-exec): STATUS: deployed
aws_instance.microk8s_instance (remote-exec): REVISION: 1
aws_instance.microk8s_instance (remote-exec): TEST SUITE: None
aws_instance.microk8s_instance (remote-exec): CVMFS installed successfully
aws_instance.microk8s_instance (remote-exec): Deploying CVMFS PVC
aws_instance.microk8s_instance (remote-exec): persistentvolumeclaim/cvmfs created
aws_instance.microk8s_instance (remote-exec): CVMFS PVC deployed successfully
aws_instance.microk8s_instance (remote-exec): Deploying CVMFS test pod
aws_instance.microk8s_instance (remote-exec): pod/cvmfs-demo created
aws_instance.microk8s_instance (remote-exec): CVMFS test pod deployed successfully
aws_instance.microk8s_instance: Creation complete after 2m53s [id=i-0545cfb13070491e5]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should start to see things happening if everything is working correctly. If errors occur, please ensure that Terraform has been installed correctly and your AWS credentials are setup. We will also walk through how to get around of the issues later in this post.&lt;/p&gt;

&lt;h2&gt;Connecting to Your MicroK8s EC2 Instance&lt;/h2&gt;

&lt;p&gt;After deploying an EC2 instance to host our MicroK8s cluster, the next step is setting up a seamless connection to manage it. This is a simple Bash script designed to connect to the EC2 instance via SSH and download the Kubernetes configuration file for local access. With this configuration file, we can use tools like Lens to manage and monitor the cluster in a user-friendly environment.&lt;/p&gt;

&lt;h3&gt;Script Overview: &lt;code&gt;connect.sh&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;connect.sh&lt;/code&gt; script automates two main tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Retrieve the EC2 Instance&amp;rsquo;s Public IP&lt;/strong&gt;:
Using AWS CLI commands, the script identifies the public IP address of the EC2 instance by filtering based on a specified tag name (e.g., &lt;code&gt;microk8s-dev-instance&lt;/code&gt;). This is particularly useful in environments with multiple instances, as it allows you to pinpoint the right one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Download Kubernetes Config for Local Access&lt;/strong&gt;:
Once the IP address is identified, the script uses &lt;code&gt;scp&lt;/code&gt; to download the &lt;code&gt;config-public&lt;/code&gt; file from the MicroK8s instance to your local environment. This configuration allows tools like Lens to connect to the Kubernetes cluster and view all pods and resources through an intuitive interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect via SSH&lt;/strong&gt;:
Finally, the script initiates an SSH connection to the instance, providing access to the EC2 server and any further on-instance configurations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Script Details&lt;/h3&gt;

&lt;p&gt;Let’s walk through the script. Save it as &lt;code&gt;connect.sh&lt;/code&gt; and customize it with your own key pair path, AWS region, and instance tag name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Set your AWS region&lt;/span&gt;
&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;  &lt;span class="c"&gt;# Change this to the region where your EC2 instance is running&lt;/span&gt;

&lt;span class="c"&gt;# Set your EC2 key pair path&lt;/span&gt;
&lt;span class="nv"&gt;KEY_PAIR_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"../devbox-key-pair.pem"&lt;/span&gt;  &lt;span class="c"&gt;# Change this to the correct path to your key pair&lt;/span&gt;

&lt;span class="c"&gt;# Set your EC2 tag name to find the instance&lt;/span&gt;
&lt;span class="nv"&gt;INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"microk8s-dev-instance"&lt;/span&gt;

&lt;span class="c"&gt;# Get the public IP of the instance with the tag name 'microk8s-dev-instance'&lt;/span&gt;
&lt;span class="nv"&gt;INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=tag:Name,Values=&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"Name=instance-state-name,Values=running"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Reservations[*].Instances[*].PublicIpAddress"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Check if an IP address was found&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instance with tag name '&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_TAG_NAME&lt;/span&gt;&lt;span class="s2"&gt;' is not running or doesn't exist."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Connect to the instance via SSH&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connecting to EC2 instance with IP: &lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

scp &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_PAIR_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ubuntu@&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;:/home/ubuntu/.kube/config-public &lt;span class="nb"&gt;.&lt;/span&gt;  &lt;span class="c"&gt;# Download kube config&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_PAIR_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ubuntu@&lt;span class="nv"&gt;$INSTANCE_PUBLIC_IP&lt;/span&gt;  &lt;span class="c"&gt;# Connect to the instance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Ensuring Everything is Running&lt;/h2&gt;

&lt;p&gt;Once we create everything with &lt;a href="http://create.sh"&gt;&lt;code&gt;create.sh&lt;/code&gt;&lt;/a&gt; and  connect to the instance using &lt;code&gt;connect.sh&lt;/code&gt;, we should be able to see the following&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image01.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;p&gt;Check to see if all the required pods are running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should see the following&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image02.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;p&gt;and &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image03.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;p&gt;By downloading the Kubernetes &lt;code&gt;config-public&lt;/code&gt; file to your local machine, you can use Lens or similar Kubernetes management tools to gain a graphical view of your cluster. This simplifies monitoring and managing your pods, services, and resources, offering a clearer and more accessible experience than using command-line tools alone.&lt;/p&gt;

&lt;p&gt;We can also see the &lt;code&gt;config-public&lt;/code&gt; in our current working directory in the docker container, we can copy and paste the config into Lens to connect to it. We will then be able to see the NFS and CVMFS drivers up and running. &lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image04.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;h2&gt;Issues&lt;/h2&gt;

&lt;p&gt;If you DO NOT see the CVMFS drivers OR the Test Pod running, then there may have been an issue with the remote provisioning to get all the drivers up and running. This is a common occurrence so you can also do the following&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check to see if the repo is in the current directory by doing &lt;code&gt;ls&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image05.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter the following command to manually install the CVMFS Drivers&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s helm3 &lt;span class="nb"&gt;install &lt;/span&gt;cvmfs /home/ubuntu/cvmfs-csi/deployments/helm/cvmfs-csi &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-f&lt;/span&gt; cvmfs-values.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;You should see the following&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image06.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can check to see if the pods are running by doing&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you should see the following&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image07.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now deploy the PVC and the Test Pod by doing the following in the command prompt&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; cvmfs-pvc.yaml &lt;span class="nt"&gt;--validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false
&lt;/span&gt;microk8s kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; cvmfs-demo-pod.yaml &lt;span class="nt"&gt;--validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and you should now see once the container is created&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image08.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;h2&gt;The Main Event&lt;/h2&gt;

&lt;p&gt;This is the moment we have all been waiting for, all of the tireless config creations and changes and head banging, we can finally see if everything is configured properly. If you followed every step in the guide and used all of the scripts we have provided, there is a 99% chance that this just all works (We are very sorry for the 1%, please do reach out if you require help)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let’s SSH into the demo pod by running&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; cvmfs-demo &lt;span class="nt"&gt;--&lt;/span&gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image09.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do an &lt;code&gt;ls&lt;/code&gt; to make sure you have the &lt;code&gt;/cvmfs&lt;/code&gt; folder in your root&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image10.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;/cvmfs&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;Do another &lt;code&gt;ls&lt;/code&gt; to see if anything is there (There should not be)&lt;/li&gt;
&lt;li&gt;BUT now we can do &lt;code&gt;ls -l atlas.cern.ch&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;What is &lt;code&gt;atlas.cern.ch&lt;/code&gt;, you can read more about the Atlas project here &lt;a href="https://atlas.cern/Discover/Detector"&gt;https://atlas.cern/Discover/Detector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;AND BOOM, we see something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/blog/2025/01/14/putting-it-all-together/image11.png" alt="image.png" /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can now go into the folder and have access to all the resources found in this repository&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@cvmfs-demo:/cvmfs# ls
atlas.cern.ch  cvmfs-config.cern.ch
root@cvmfs-demo:/cvmfs# cd atlas.cern.ch/
root@cvmfs-demo:/cvmfs/atlas.cern.ch# ls
repo
root@cvmfs-demo:/cvmfs/atlas.cern.ch# cd repo/
root@cvmfs-demo:/cvmfs/atlas.cern.ch/repo# ls
ATLASLocalRootBase  benchmarks  conditions  containers  dev  sw  test  tools  tutorials
root@cvmfs-demo:/cvmfs/atlas.cern.ch/repo# 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What Just Happened?&lt;/h2&gt;

&lt;p&gt;How did our pod go from empty to housing a repository full of tools for the ATLAS project? That’s the power of CVMFS at work. The CVMFS drivers we installed earlier are responsible for managing these dynamic volumes. When we access the &lt;code&gt;/cvmfs&lt;/code&gt; mount, it signals the drivers to retrieve the requested resources. The drivers, with the help of FUSE (Filesystem in Userspace), seamlessly pull in the data needed for the repository, giving us instant access to specialized tools and data.&lt;/p&gt;

&lt;h2&gt;So What’s Next?&lt;/h2&gt;

&lt;p&gt;Now that we’ve accessed the ATLAS project’s repository through CVMFS, you might be wondering how this setup could benefit our own projects. Imagine having an instantly accessible, version-controlled library of tools, datasets, and configurations that’s always up-to-date and readily available to anyone who needs it. This is the power of CVMFS. It&amp;rsquo;s not only useful for accessing existing repositories like ATLAS but also for hosting and sharing your own resources.&lt;/p&gt;

&lt;h3&gt;How Can We Use This for Our Own Projects?&lt;/h3&gt;

&lt;p&gt;CVMFS allows us to create and manage custom repositories where we can package our own tools, scripts, datasets, and even entire software environments. By hosting these resources on a CVMFS repository, we can ensure consistency across development and production environments and make collaboration smoother for teams working with large or complex data.&lt;/p&gt;

&lt;p&gt;In the next part of our series, we’ll explore a live use case: &lt;strong&gt;The Galaxy Project&lt;/strong&gt;. This project leverages CVMFS to manage and distribute bioinformatics tools, datasets, and workflows. We’ll dive into how Galaxy uses CVMFS to ensure that researchers have access to the latest tools and resources, and how you can apply similar principles to streamline and scale your own projects. Stay tuned!&lt;/p&gt;

&lt;h2&gt;Optional: Teardown of Instance&lt;/h2&gt;

&lt;p&gt;Here is a small script to tear down your entire EC2 instance once you are done practicing. You can use this in any terraform setup&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Destroying Terraform-managed resources..."&lt;/span&gt;
terraform init 
terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt; 

&lt;span class="c"&gt;# Optional: Remove all terraform state files&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; terraform.tfstate
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; terraform.tfstate.backup
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; terraform.tfstate.lock.info
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .terraform.lock.hcl
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .terraform
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Terraform resources destroyed."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Repo for FULL Setup &lt;a href="https://github.com/lablytics/cvmfs-csi/tree/master/devops"&gt;https://github.com/lablytics/cvmfs-csi/tree/master/devops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next Section: &lt;a href="/blog/2025/01/15/use-case-the-galaxy-project/"&gt;Use Case: The Galaxy Project&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
</feed>
