<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>NACAMURA Mitsuhiro</title>
    <subtitle>NACAMURA Mitsuhiro&#39;s website</subtitle>
    <link rel="self" type="application/atom+xml" href="https://m15a.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://m15a.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-09T00:00:00+00:00</updated>
    <id>https://m15a.dev/atom.xml</id>
    <entry xml:lang="en">
        <title>Housekeeping My Neovim Configuration</title>
        <published>2025-12-07T00:00:00+00:00</published>
        <updated>2026-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>NACAMURA Mitsuhiro</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://m15a.dev/posts/updating-config-nvim/"/>
        <id>https://m15a.dev/posts/updating-config-nvim/</id>
        
        <content type="html" xml:base="https://m15a.dev/posts/updating-config-nvim/">&lt;h2 id=&quot;the-end-of-stagnation&quot;&gt;The End of Stagnation&lt;/h2&gt;
&lt;p&gt;I used to be a passionate tinkerer when it came to Vim/Neovim
configuration. Lately, however, that passion has cooled. The last time
I significantly touched my config was back in January of this year.
Since then, I&#39;ve been using Neovim daily without making any fixes or
updates.&lt;/p&gt;
&lt;p&gt;After nearly a year of stagnation, I happened to update the lock file
of my configuration, which is managed using &lt;a rel=&quot;external&quot; href=&quot;https://nixos.org/&quot;&gt;Nix&lt;/a&gt;. This triggered
a realization: it was time to clear out the accumulating deprecation
warnings and modernize my setup to keep up with the evolving Neovim
scene. Here is a log of my &quot;Spring Cleaning&quot; in December.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;updates-migrations&quot;&gt;Updates &amp;amp; Migrations&lt;/h2&gt;
&lt;h3 id=&quot;addressing-checkhealth-warnings&quot;&gt;Addressing &lt;code&gt;:checkhealth&lt;/code&gt; Warnings&lt;/h3&gt;
&lt;p&gt;For a while (before updating the lock file), I had been ignoring a
persistent warning in my &lt;code&gt;:messages&lt;/code&gt; regarding the usage of the
obsolete &lt;code&gt;vim.fn.sign_define&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;text&quot;&gt;- ⚠️ WARNING Defining diagnostic signs with :sign-define or sign_define() is deprecated. Feature will be removed in Nvim 0.12
  - ADVICE:
    - use vim.diagnostic.config() instead.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I finally decided to stop ignoring it. After reading
&lt;code&gt;:h diagnostic-signs&lt;/code&gt;, I refactored the code to use the modern
&lt;code&gt;vim.diagnostic.config&lt;/code&gt; API:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;diff&quot;&gt;diff --git a/lua/m15a/plugins/lspconfig/init.lua b/lua/m15a/plugins/lspconfig/init.lua
index 3cfbd2124d..8644fedc68 100644
--- a/lua/m15a/plugins/lspconfig/init.lua
+++ b/lua/m15a/plugins/lspconfig/init.lua
@@ -97,12 +97,19 @@
 end

 local function setup_diagnostic_signs()
-   -- Change diagnostic signs in the gutter:
-   -- https://github.com/neovim/nvim-lspconfig/wiki/UI-Customization#change-diagnostic-symbols-in-the-sign-column-gutter
+   local signs = {
+      text = {},
+      texthl = {},
+      numhl = {},
+   }
    for type, text in pairs(assets.fonts.diagnostic) do
+      local severity = vim.diagnostic.severity[type:gsub(&amp;#39;^%l+&amp;#39;, string.upper)]
       local hl = &amp;#39;DiagnosticSign&amp;#39; .. type:gsub(&amp;#39;^%l&amp;#39;, string.upper)
-      vim.fn.sign_define(hl, { text = text, texthl = hl, numhl = hl })
+      signs.text[severity] = text
+      signs.texthl[severity] = hl
+      signs.numhl[severity] = hl
    end
+   vim.diagnostic.config { signs = signs }
 end

 function M.setup()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;modernizing-nvim-lspconfig&quot;&gt;Modernizing &lt;a rel=&quot;external&quot; href=&quot;https://github.com/neovim/nvim-lspconfig&quot;&gt;nvim-lspconfig&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &quot;framework&quot; API of &lt;a rel=&quot;external&quot; href=&quot;https://github.com/neovim/nvim-lspconfig&quot;&gt;nvim-lspconfig&lt;/a&gt; was
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/neovim/nvim-lspconfig/pull/4077&quot;&gt;deprecated (Sep 18, 2025)&lt;/a&gt;,
now that Neovim supports
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/neovim/neovim/pull/31031&quot;&gt;&lt;code&gt;vim.lsp.config&lt;/code&gt; (as of Dec 11, 2024)&lt;/a&gt;.
To align with this shift, I updated the legacy API calls in my config:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Replaced &lt;code&gt;require(&quot;lspconfig&quot;)&lt;/code&gt; with &lt;code&gt;vim.lsp.config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Updated the server setup logic.&lt;/li&gt;
&lt;li&gt;Ensured servers are explicitly enabled.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code data-lang=&quot;diff&quot;&gt;diff --git a/lua/m15a/plugins/lspconfig/init.lua b/lua/m15a/plugins/lspconfig/init.lua
index a6f8dbbfce..d99c110643 100644
--- a/lua/m15a/plugins/lspconfig/init.lua
+++ b/lua/m15a/plugins/lspconfig/init.lua
@@ -1,6 +1,5 @@
 local namespace = ...

-local lspconfig = require &amp;#39;lspconfig&amp;#39;
 local tbuiltin = require &amp;#39;telescope.builtin&amp;#39;
 local assets = require &amp;#39;m15a.assets&amp;#39;
 local keymap = require &amp;#39;m15a.keymap&amp;#39;
@@ -27,7 +26,8 @@
 local function setup_servers()
    for server, cmd in pairs(servers) do
       if vim.fn.executable(cmd) &amp;gt; 0 then
-         lspconfig[server].setup(require_server_config(server))
+         vim.lsp.config(server, require_server_config(server))
+         vim.lsp.enable(server)
       end
    end
 end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;optimizing-telescope-nvim-keybindings&quot;&gt;Optimizing &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nvim-telescope/telescope.nvim&quot;&gt;telescope.nvim&lt;/a&gt; Keybindings&lt;/h3&gt;
&lt;p&gt;I reconsidered my historical Telescope key mappings to improve
ergonomics. Previously, opening a picker required three keystrokes
(e.g., &lt;code&gt;&amp;lt;Leader&amp;gt;ef&lt;/code&gt;, &lt;code&gt;&amp;lt;Leader&amp;gt;eg&lt;/code&gt;, etc.), as configured below:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;lua&quot;&gt;vim.api.nvim_set_keymap(&amp;quot;&amp;quot;, &amp;quot;[telescope]&amp;quot;, &amp;quot;&amp;lt;Nop&amp;gt;&amp;quot;, { noremap = true })
vim.api.nvim_set_keymap(&amp;quot;&amp;quot;, &amp;quot;&amp;lt;Leader&amp;gt;e&amp;quot;, &amp;quot;[telescope]&amp;quot;, {})
vim.keymap.set(&amp;quot;n&amp;quot;, &amp;quot;[telescope]f&amp;quot;, require(&amp;quot;telescope.builtin&amp;quot;).find_files)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have now reduced this to just two keystrokes: &lt;code&gt;&amp;lt;Leader&amp;gt;f&lt;/code&gt;,
&lt;code&gt;&amp;lt;Leader&amp;gt;g&lt;/code&gt;, etc. It feels much faster and more direct.&lt;/p&gt;
&lt;h3 id=&quot;pruning-nvim-treesitter-textobjects&quot;&gt;Pruning &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nvim-treesitter/nvim-treesitter-textobjects&quot;&gt;nvim-treesitter-textobjects&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This plugin offers a massive number of syntax textobjects.
In the past, I blindly mapped almost all of them.
However, in practice, I only use a handful.
I decided to keep only the essentials:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;lua&quot;&gt;         keymaps = {
            [&amp;quot;ik&amp;quot;] = &amp;quot;@class.inner&amp;quot;,
            [&amp;quot;ak&amp;quot;] = &amp;quot;@class.outer&amp;quot;,
            [&amp;quot;ic&amp;quot;] = &amp;quot;@comment.inner&amp;quot;,
            [&amp;quot;ac&amp;quot;] = &amp;quot;@comment.outer&amp;quot;,
            [&amp;quot;im&amp;quot;] = &amp;quot;@function.inner&amp;quot;,
            [&amp;quot;am&amp;quot;] = &amp;quot;@function.outer&amp;quot;,
            [&amp;quot;ia&amp;quot;] = &amp;quot;@parameter.inner&amp;quot;,
            [&amp;quot;aa&amp;quot;] = &amp;quot;@parameter.outer&amp;quot;,
         },
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;replacing-feline-nvim&quot;&gt;Replacing &lt;a rel=&quot;external&quot; href=&quot;https://github.com/famiu/feline.nvim&quot;&gt;feline.nvim&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The repository &lt;a rel=&quot;external&quot; href=&quot;https://github.com/freddiehaddad/feline.nvim&quot;&gt;freddiehaddad/feline.nvim&lt;/a&gt;—a previously maintained fork
of the original &lt;a rel=&quot;external&quot; href=&quot;https://github.com/famiu/feline.nvim&quot;&gt;feline.nvim&lt;/a&gt;—was deleted. I was a fan of this
minimalist statusline framework, but with the repo gone, migration
was necessary. I decided to switch to &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nvim-lualine/lualine.nvim&quot;&gt;lualine.nvim&lt;/a&gt;, which is another
minimal (and widely-adopted) statusline plugin.&lt;/p&gt;
&lt;h3 id=&quot;sticking-with-dressing-nvim&quot;&gt;Sticking with &lt;a rel=&quot;external&quot; href=&quot;https://github.com/stevearc/dressing.nvim&quot;&gt;dressing.nvim&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https://github.com/stevearc/dressing.nvim&quot;&gt;dressing.nvim&lt;/a&gt; repository has been archived.
I read &lt;a rel=&quot;external&quot; href=&quot;https://github.com/stevearc/dressing.nvim/issues/190&quot;&gt;the issue&lt;/a&gt; where the author
explains the decision:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[F]olke came out with snacks.nvim and it contains both a fuzzy
vim.ui.select implementation and a good vim.ui.input. Now, at long
last, I can say that this plugin has outlived its usefulness and
retire it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I looked at the suggested successor, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/folke/snacks.nvim&quot;&gt;snacks.nvim&lt;/a&gt;. However, it
comes with far more features than I need. I prefer a simpler plugin for
this functionality. For now, I have decided to keep using the archived
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/stevearc/dressing.nvim&quot;&gt;dressing.nvim&lt;/a&gt; until a more focused alternative appears.&lt;/p&gt;
&lt;h3 id=&quot;migrating-to-lastplace-nvim&quot;&gt;Migrating to &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nxhung2304/lastplace.nvim&quot;&gt;lastplace.nvim&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I migrated from &lt;a rel=&quot;external&quot; href=&quot;https://github.com/ethanholz/nvim-lastplace&quot;&gt;nvim-lastplace&lt;/a&gt; to &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nxhung2304/lastplace.nvim&quot;&gt;lastplace.nvim&lt;/a&gt;. Although I knew
the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/ethanholz/nvim-lastplace&quot;&gt;nvim-lastplace&lt;/a&gt; repository had been archived for a while, I kept
using it simply because it worked. This update brings me back to a
maintained version.&lt;/p&gt;
&lt;h2 id=&quot;removals&quot;&gt;Removals&lt;/h2&gt;
&lt;h3 id=&quot;markview-nvim&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/OXY2DEV/markview.nvim&quot;&gt;markview.nvim&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I decided to remove &lt;a rel=&quot;external&quot; href=&quot;https://github.com/OXY2DEV/markview.nvim&quot;&gt;markview.nvim&lt;/a&gt;. I realized that I dislike heavily
concealed text. The dynamic width changes that occur when switching
between normal and insert modes were becoming visually distracting.&lt;/p&gt;
&lt;h3 id=&quot;gitsigns-nvim&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/lewis6991/gitsigns.nvim&quot;&gt;gitsigns.nvim&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I removed &lt;a rel=&quot;external&quot; href=&quot;https://github.com/lewis6991/gitsigns.nvim&quot;&gt;gitsigns.nvim&lt;/a&gt; in favor of &lt;a rel=&quot;external&quot; href=&quot;https://github.com/jj-vcs/jj&quot;&gt;Jujutsu&lt;/a&gt;, which I have recently
started using as my Git frontend.&lt;/p&gt;
&lt;h2 id=&quot;additions&quot;&gt;Additions&lt;/h2&gt;
&lt;h3 id=&quot;nvim-ts-autotag&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/windwp/nvim-ts-autotag&quot;&gt;nvim-ts-autotag&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I had overlooked it previously, but it is incredibly helpful when
editing HTML.&lt;/p&gt;
&lt;h3 id=&quot;nvim-origami&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/chrisgrieser/nvim-origami&quot;&gt;nvim-origami&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have seldom used folding and its plugins. My preference has been
that code should be split into small enough files so that folding
becomes unnecessary.&lt;/p&gt;
&lt;p&gt;However, I realized that for certain tasks—specifically scripts for
deploying to pipeline orchestrators that prohibit external packaging—I
am often forced to put many modules into a single script file. In these
cases, folding helps me. I decided to introduce &lt;a rel=&quot;external&quot; href=&quot;https://github.com/chrisgrieser/nvim-origami&quot;&gt;nvim-origami&lt;/a&gt; to
handle this.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;next-time&quot;&gt;Next Time?&lt;/h2&gt;
&lt;p&gt;It took about a week of spare time to finish this configuration
overhaul. My next major update will likely happen after
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/nvim-treesitter/nvim-treesitter&quot;&gt;nvim-treesitter&lt;/a&gt; releases its &lt;code&gt;main&lt;/code&gt; branch, as that is expected to
bring several breaking changes.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>&quot;As Cloud-like as Possible&quot; Data Science: Local MLOps with Docker Compose</title>
        <published>2025-11-24T00:00:00+00:00</published>
        <updated>2026-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>NACAMURA Mitsuhiro</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://m15a.dev/posts/acap-datasci-env/"/>
        <id>https://m15a.dev/posts/acap-datasci-env/</id>
        
        <content type="html" xml:base="https://m15a.dev/posts/acap-datasci-env/">&lt;h2 id=&quot;emulates-cloud-native-mlops-locally&quot;&gt;Emulates cloud-native MLOps locally&lt;/h2&gt;
&lt;p&gt;I have built a data science environment that allows me to construct
data pipelines and manage machine learning experiments on a local PC,
while providing a user experience that is as cloud-like as possible.&lt;/p&gt;
&lt;p&gt;According to Gemini:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This environment functions as a sandbox for learning a
&quot;cloud-native development style&quot; by replacing major components
used in cloud environments—such as S3, orchestrators, and ML
tracking services—with local Docker containers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alright. If you say so, this serves as educational content.&lt;/p&gt;
&lt;p&gt;The source code (&lt;code&gt;docker-compose.yaml&lt;/code&gt;, etc.) is available on &lt;a rel=&quot;external&quot; href=&quot;https://github.com/m15a/acap-datasci-env&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;system-components&quot;&gt;System components&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Data store: &lt;a rel=&quot;external&quot; href=&quot;https://www.versity.com/products/versitygw/&quot;&gt;Versity S3 Gateway&lt;/a&gt; emulates S3 storage.&lt;/li&gt;
&lt;li&gt;Source code repository: Just a Git daemon.&lt;/li&gt;
&lt;li&gt;Pipeline: &lt;a rel=&quot;external&quot; href=&quot;https://www.prefect.io/&quot;&gt;Prefect&lt;/a&gt; orchestrates pipelines.&lt;/li&gt;
&lt;li&gt;Experiment management: &lt;a rel=&quot;external&quot; href=&quot;https://mlflow.org/&quot;&gt;MLflow&lt;/a&gt; tracks models, parameters, and results.&lt;/li&gt;
&lt;li&gt;Notebook: &lt;a rel=&quot;external&quot; href=&quot;https://marimo.io/&quot;&gt;Marimo&lt;/a&gt; for interactive development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I initially used &lt;a rel=&quot;external&quot; href=&quot;https://www.min.io/&quot;&gt;MinIO&lt;/a&gt; as the S3 alternative, but since they have
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/minio/minio/blob/master/README.md#source-only-distribution&quot;&gt;stopped distributing binaries and Docker images&lt;/a&gt;,
I switched to the Versity S3 gateway to access the file system via the
S3 protocol.&lt;/p&gt;
&lt;p&gt;The system components collaborate as shown in the following diagram:&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
graph TD
    classDef host fill:#f5f5f5,stroke:#333,stroke-width:2px;
    classDef app fill:#e1f5fe,stroke:#0277bd,stroke-width:2px;
    classDef infra fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;

    subgraph host_net [Host Environment]
        host[PC]:::host
    end

    subgraph docker_net [Docker Network]
        prefect_server(Prefect Server):::app
        prefect_worker(Prefect Worker):::app
        mlflow(MLflow Server):::app

        git[(Git Daemon)]:::infra
        versitygw[(Versity S3)]:::infra
        postgres[(PostgreSQL)]:::infra

        host --&gt;|Code| git --&gt;|Code| prefect_worker
        host --&gt;|Web UI / CLI| prefect_server
        host --&gt;|Web UI| mlflow
        host &lt;--&gt;|Data| versitygw

        prefect_server -.-|Backend| postgres
        prefect_server --&gt;|Run| prefect_worker

        prefect_worker --&gt;|API| mlflow
        prefect_worker &lt;--&gt;|Data| versitygw

        mlflow -.-|Backend| postgres
        mlflow --&gt;|Artifacts| versitygw
    end
&lt;/pre&gt;
&lt;p&gt;(Marimo is omitted for clarity as it is loosely coupled with the
rest of the system.)&lt;/p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;
&lt;p&gt;The steps for running experiments are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;(If necessary) Upload the data to S3 (&lt;code&gt;http://localhost:7070&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Implement the Prefect workflow (&lt;code&gt;experiments/*.py&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push&lt;/code&gt; the code to the Git repo (&lt;code&gt;http:localhost:9010/repo&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Deploy the workflow to Prefect (&lt;code&gt;prefect deploy&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Run the workflow via Prefect (&lt;code&gt;prefect deployment run&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Check the experiment results in MLflow (&lt;code&gt;http://localhost:5001&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Detailed prerequisite steps, such as setting environment variables,
are written in the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/m15a/acap-datasci-env&quot;&gt;GitHub&lt;/a&gt; README.&lt;/p&gt;
&lt;h3 id=&quot;implementing-prefect-workflows&quot;&gt;Implementing Prefect workflows&lt;/h3&gt;
&lt;p&gt;Define your experiment workflows and tasks using Prefect decorators
(&lt;code&gt;@flow&lt;/code&gt;, &lt;code&gt;@task&lt;/code&gt;). Insert MLflow functions such as
&lt;code&gt;mlflow.log_params()&lt;/code&gt; to track everything in MLflow!&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;@flow
def example(n_tests: int = 1) -&amp;gt; None:
    &amp;quot;&amp;quot;&amp;quot;An example experiment as a Prefect flow with MLflow tracking.&amp;quot;&amp;quot;&amp;quot;
    mlflow.set_tracking_uri(MLFLOW_TRACKING_URI)
    mlflow.set_experiment(f&amp;quot;example-experiment-{datetime.now()}&amp;quot;)

    df = pd.read_csv(&amp;quot;s3://data/iris.csv&amp;quot;)
    x = df.drop(columns=[&amp;quot;target&amp;quot;, &amp;quot;species&amp;quot;])
    y = df[&amp;quot;target&amp;quot;]
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)

    test_data = x_test.copy()
    test_data[&amp;quot;target&amp;quot;] = y_test
    test_dataset = mlflow.data.from_pandas(test_data, targets=&amp;quot;target&amp;quot;)

    @task
    def run(n_estimators, criterion) -&amp;gt; None:
        &amp;quot;&amp;quot;&amp;quot;A run in the experiment.&amp;quot;&amp;quot;&amp;quot;
        with mlflow.start_run():
            params = {&amp;quot;n_estimators&amp;quot;: n_estimators, &amp;quot;criterion&amp;quot;: criterion}
            mlflow.log_params(params)
            model = RandomForestClassifier(**params)
            model.fit(x_train, y_train)
            model_info = mlflow.sklearn.log_model(model, input_example=x_train)
            mlflow.models.evaluate(
                model=model_info.model_uri,
                data=test_dataset,
                model_type=&amp;quot;classifier&amp;quot;,
            )

    for n_estimators in [2**i for i in range(5, 8)]:
        for criterion in [&amp;quot;gini&amp;quot;, &amp;quot;entropy&amp;quot;, &amp;quot;log_loss&amp;quot;]:
            for _ in range(n_tests):
                run(n_estimators, criterion)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deploying-and-running-prefect-workflows&quot;&gt;Deploying and running Prefect workflows&lt;/h3&gt;
&lt;p&gt;Register the workflow with the Prefect server by declaring the deployment
configuration in &lt;code&gt;prefect.yaml&lt;/code&gt; and running &lt;code&gt;prefect deploy&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;deployments:
  - name: experiment
    tags: [test]
    parameters:
      n_tests: 5
    entrypoint: experiments/example.py:example
    work_pool:
      name: sandbox
      job_variables: {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After deploying, run it with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;prefect deployment run example/experiment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can view the execution status in real-time by accessing the
Prefect Web UI (&lt;a rel=&quot;external&quot; href=&quot;http://localhost:4200/runs&quot;&gt;http://localhost:4200/runs&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&quot;checking-and-analyzing-results-in-mlflow&quot;&gt;Checking and analyzing results in MLflow&lt;/h3&gt;
&lt;p&gt;Access the MLflow Web UI (&lt;a rel=&quot;external&quot; href=&quot;http://localhost:5001&quot;&gt;http://localhost:5001&lt;/a&gt;) to review the
tracked experiment results and perform comparative analysis based on
parameters.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I have created a local environment that feels &quot;as cloud-like as
possible.&quot; Adding something like a data lake (e.g., &lt;a rel=&quot;external&quot; href=&quot;https://ducklake.select/&quot;&gt;DuckLake&lt;/a&gt;) would
likely make it feel even more like the cloud.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Translated from the original post
at &lt;a rel=&quot;external&quot; href=&quot;https://m15a.dev/ja/posts/acap-datasci-env/&quot;&gt;https://m15a.dev/ja/posts/acap-datasci-env/&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How Much Smaller Are NixNG Container Images Compared to NixOS, Really?</title>
        <published>2025-11-08T00:00:00+00:00</published>
        <updated>2025-11-09T00:00:00+00:00</updated>
        
        <author>
          <name>NACAMURA Mitsuhiro</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://m15a.dev/posts/nixng-image-size/"/>
        <id>https://m15a.dev/posts/nixng-image-size/</id>
        
        <content type="html" xml:base="https://m15a.dev/posts/nixng-image-size/">&lt;h2 id=&quot;nixng&quot;&gt;NixNG&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/nix-community/NixNG&quot;&gt;NixNG&lt;/a&gt; is a Linux distribution currently under development, derived
from &lt;a rel=&quot;external&quot; href=&quot;https://nixos.org/&quot;&gt;NixOS&lt;/a&gt;. It is being positioned as a lightweight alternative to
NixOS, specifically targeting container environments.&lt;/p&gt;
&lt;p&gt;The project achieves its reduced size by omitting systemd from
NixOS and minimizing the default package set. Since NixOS container
images are a bit heavy, a lighter alternative is definitely welcome.
Dropping systemd also opens up possibilities for using
lighter-weight init systems like runit, which I find to be an exciting
prospect.&lt;/p&gt;
&lt;p&gt;But just how much smaller is NixNG in practice, compared to an image
based on the original NixOS? I created both images to compare their
sizes.&lt;/p&gt;
&lt;h2 id=&quot;the-experiment&quot;&gt;The experiment&lt;/h2&gt;
&lt;p&gt;The official Nix image is &lt;a rel=&quot;external&quot; href=&quot;https://hub.docker.com/r/nixos/nix/&quot;&gt;available on DockerHub&lt;/a&gt;. I built
a comparable NixNG image that includes only the Nix package manager and
then compared the sizes of the two.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Distro&lt;/th&gt;&lt;th&gt;Package&lt;/th&gt;&lt;th&gt;Base image/Build&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Official Nix&lt;/td&gt;&lt;td&gt;Nix 2.32.2&lt;/td&gt;&lt;td&gt;DockerHub &lt;code&gt;nixos/nix:2.32.2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;NixNG&lt;/td&gt;&lt;td&gt;Nix 2.32.2&lt;/td&gt;&lt;td&gt;Built using the method below&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I ensured that the platform was consistent: &lt;code&gt;x86_64-linux&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;nixng-build-flake-setup&quot;&gt;NixNG build flake setup&lt;/h3&gt;
&lt;p&gt;I set up a minimal flake project for the test.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;flake.nix&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;nix&quot;&gt;{
  inputs = {
    nixng.url = &amp;quot;github:nix-community/NixNG&amp;quot;;
    nixpkgs.url = &amp;quot;github:NixOS/nixpkgs/nixpkgs-unstable&amp;quot;;
  };
  outputs =
    {
      self,
      nixng,
      nixpkgs,
      ...
    }:
    {
      examples.nixng-nix = import ./examples/nixng-nix.nix {
        inherit (nixng) nglib;
        inherit nixpkgs;
        system = &amp;quot;x86_64-linux&amp;quot;;
      };
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The system of interest for comparison is defined in
&lt;code&gt;examples.nixng-nix&lt;/code&gt;, based on
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/nix-community/NixNG/tree/master/examples/nix&quot;&gt;an example from the NixNG repository&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;examples/nixng-nix.nix&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;nix&quot;&gt;{
  nglib,
  nixpkgs,
  system,
}:

nglib.makeSystem {
  inherit nixpkgs;
  inherit system;
  name = &amp;quot;nixng-nix&amp;quot;;
  config =
    { pkgs, ... }:
    {
      dumb-init = {
        enable = true;
        type.shell = { };
      };
      nix = {
        enable = true;
        package = pkgs.nixVersions.nix_2_32;
        config = {
          experimental-features = [
            &amp;quot;nix-command&amp;quot;
            &amp;quot;flakes&amp;quot;
          ];
          sandbox = false;
        };
      };
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;build-and-load-the-oci-image&quot;&gt;Build and load the OCI image&lt;/h3&gt;
&lt;p&gt;I generated and loaded the Docker image tar file:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;$ docker run --rm --platform linux/amd64 -v $(pwd):/work -w /work \
    nixos/nix:2.32.2 bash -c &amp;#39;
    {
        echo &amp;quot;filter-syscalls = false&amp;quot;
        echo &amp;quot;experimental-features = nix-command flakes&amp;quot;
    } &amp;gt;&amp;gt; /etc/nix/nix.conf
    nix build .#examples.nixng-nix.config.system.build.ociImage.stream
    ./result &amp;gt; nixng-nix-image.tar
    &amp;#39;

$ docker image load -i nixng-nix-image.tar 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: Since I use Docker Desktop on an ARM Mac, I
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/DeterminateSystems/nix-installer/issues/324&quot;&gt;had to add &lt;code&gt;filter-syscalls = false&lt;/code&gt; to the Nix configuration&lt;/a&gt;
to avoid an error during the build.&lt;/p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;
&lt;h3 id=&quot;image-sizes&quot;&gt;Image sizes&lt;/h3&gt;
&lt;p&gt;With the official Nix image already available, I checked the sizes:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nixos/nix    2.32.2    f9b3c7811e27   55 years ago   436MB
nixos/nix    latest    f9b3c7811e27   55 years ago   436MB
nixng-nix    latest    26361447b07f   55 years ago   356MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The official image was &lt;strong&gt;436MB&lt;/strong&gt;, while the NixNG image came in
at &lt;strong&gt;356MB&lt;/strong&gt;. That&#39;s a size reduction of &lt;strong&gt;80MB&lt;/strong&gt; (approximately
&lt;strong&gt;18%&lt;/strong&gt;).&lt;/p&gt;
&lt;h3 id=&quot;nix-store-contents&quot;&gt;&lt;code&gt;/nix/store/&lt;/code&gt; contents&lt;/h3&gt;
&lt;p&gt;I was curious to see exactly which packages were omitted.
First, I looked at the package count in the &lt;code&gt;/nix/store/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;$ docker run --platform linux/amd64 nixos/nix:2.32.2 \
    sh -c &amp;#39;ls /nix/store&amp;#39; 2&amp;gt;/dev/null | rg &amp;#39;^[a-z0-9]+-&amp;#39; | wc -l
     121
$ docker run --platform linux/amd64 nixng-nix \
    sh -c &amp;#39;ls /nix/store&amp;#39; 2&amp;gt;/dev/null | rg &amp;#39;^[a-z0-9]+-&amp;#39; | wc -l
      81
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The official Nix image contains &lt;strong&gt;121&lt;/strong&gt; packages, compared to &lt;strong&gt;81&lt;/strong&gt;
in NixNG—a reduction of &lt;strong&gt;40&lt;/strong&gt; packages.&lt;/p&gt;
&lt;p&gt;Next, I looked at the detailed differences. After preprocessing the
package names to normalize version and date differences:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;$ docker run --platform linux/amd64 nixos/nix:2.32.2 \
    sh -c &amp;#39;ls /nix/store&amp;#39; 2&amp;gt;/dev/null | rg &amp;#39;^[a-z0-9]+-&amp;#39; \
    | cut -d&amp;#39;-&amp;#39; -f2- \
    | sed -Ee &amp;#39;s#[0-9]{4}(-?[0-9]{2}(-?[0-9]{2})?)?[a-z]?#DATE#&amp;#39; \
        -e &amp;#39;s#[0-9]+\.[0-9]+([.p-][0-9]+)?#VERSION#&amp;#39; \
    | sort \
    | uniq \
    &amp;gt; nixos-nix-nix-store.txt

$ docker run --platform linux/amd64 nixng-nix \
    sh -c &amp;#39;ls /nix/store&amp;#39; 2&amp;gt;/dev/null | rg &amp;#39;^[a-z0-9]+-&amp;#39; \
    | cut -d&amp;#39;-&amp;#39; -f2- \
    | sed -Ee &amp;#39;s#[0-9]{4}(-?[0-9]{2}(-?[0-9]{2})?)?[a-z]?#DATE#&amp;#39; \
        -e &amp;#39;s#[0-9]+\.[0-9]+([.p-][0-9]+)?#VERSION#&amp;#39; \
    | sort \
    | uniq \
    &amp;gt; nixng-nix-nix-store.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comparing the diff reveals the omitted components:&lt;/p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;diff&quot;&gt;--- nixos-nix-nix-store.txt	2025-11-08 21:45:41
+++ nixng-nix-nix-store.txt	2025-11-08 21:45:48
@@ -1,6 +1,5 @@
 acl-VERSION
 attr-VERSION
-audit-VERSION-lib
 aws-c-auth-VERSION
 aws-c-cal-VERSION
 aws-c-common-VERSION
@@ -14,73 +13,45 @@
 aws-checksums-VERSION
 aws-crt-cpp-VERSION
 aws-sdk-cpp-VERSION
-base-system
 bash-interactive-VERSION
-bash-interactive-VERSION-man
 bash-VERSION
 boehm-gc-VERSION
 boost-VERSION
 brotli-VERSION-lib
 busybox-VERSION
 bzip2-VERSION
-channel-nixos
-coreutils-full-VERSION
-coreutils-VERSION
+ca-certificates.crt
 curl-VERSION
-curl-VERSION-bin
-curl-VERSION-man
-db-VERSION
-dns-root-data-DATE
-editline-VERSION
-expat-VERSION
-findutils-VERSION
+dumb-init-VERSION
+editline-VERSION-unstable-DATE
 gcc-VERSION-lib
 gcc-VERSION-libgcc
-gdbm-VERSION-lib
-gettext-VERSION
-git-minimal-VERSION
+generate-shadow
 glibc-VERSION
-glibc-VERSION-bin
-gmp-with-cxx-VERSION
-gnugrep-VERSION
-gnused-VERSION
-gnutar-VERSION
-groff-VERSION
-gzip-VERSION
-gzip-VERSION-man
+group
+hwloc-VERSION-lib
 iana-etc-DATE
+icu4c-VERSION
 keyutils-VERSION-lib
 krb5-VERSION-lib
-ldns-VERSION
-less-668
-less-668-man
 libarchive-VERSION-lib
 libblake3-VERSION
-libbsd-VERSION
-libcap-ng-VERSION
-libcap-VERSION-lib
-libcbor-VERSION
 libcpuid-VERSION
-libedit-DATE-VERSION
-libfido2-VERSION
 libgit2-VERSION-lib
 libidn2-VERSION
-libmd-VERSION
-libpipeline-VERSION
 libpsl-VERSION
 libseccomp-VERSION-lib
 libsodium-VERSION
 libssh2-VERSION
 libunistring-VERSION
-libxcrypt-VERSION
 libxml2-VERSION
-linux-pam-VERSION
 llhttp-VERSION
 lowdown-VERSION-lib
-man-db-VERSION
-manifest.nix
 ncurses-VERSION
+ndig4f4dx8bmrmyr5vfm19g02r9l9ggm-source
 nghttp2-VERSION-lib
+nghttp3-VERSION
+ngtcp2-VERSION
 nix-cmd-VERSION
 nix-expr-VERSION
 nix-fetchers-VERSION
@@ -89,32 +60,20 @@
 nix-store-VERSION
 nix-util-VERSION
 nix-VERSION
-nss-cacert-VERSION
-openssh-VERSION
+nix.conf
+nixng-nixng-nix
+onetbb-DATE.VERSION
 openssl-VERSION
+passwd
 pcre2-VERSION
-pcsclite-VERSION-lib
+profile
 publicsuffix-list-0-unstable-DATE
 readline-VERSION
-root-profile-env
 s2n-tls-VERSION
-shadow-VERSION
-source
+service-dir
 sqlite-VERSION
-systemd-minimal-libs-VERSION
-tbb-DATE.VERSION
-tbb-DATE.VERSION-dev
-tcb-VERSION
-util-linux-minimal-VERSION-bin
-util-linux-minimal-VERSION-lib
-util-linux-minimal-VERSION-login
-util-linux-minimal-VERSION-mount
-util-linux-minimal-VERSION-swap
-wget-VERSION
-which-VERSION
+tzdata-DATE
 xgcc-VERSION-libgcc
 xz-VERSION
-zlib-ng-VERSION
 zlib-VERSION
 zstd-VERSION
-zstd-VERSION-bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to the removal of systemd (&lt;code&gt;systemd-minimal-libs&lt;/code&gt;),
in NixNG, many other packages have also been omitted.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;To be blunt, the default advice for minimizing container size is
often: &lt;strong&gt;&quot;Use Alpine or Distroless!&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;However, if committed to using Nix in a project and want to
save a bit on cloud storage or bandwidth costs, NixNG should be
a viable option.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Translated from the original post
at &lt;a rel=&quot;external&quot; href=&quot;https://m15a.dev/ja/posts/nixng-image-size/&quot;&gt;https://m15a.dev/ja/posts/nixng-image-size/&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
</feed>
